Skip to content

Commit 33030b3

Browse files
[ty] linear variance inference for PEP-695 type parameters (#18713)
## Summary Implement linear-time variance inference for type variables (astral-sh/ty#488). Inspired by Martin Huschenbett's [PyCon 2025 Talk](https://www.youtube.com/watch?v=7uixlNTOY4s&t=9705s). ## Test Plan update tests, add new tests, including for mutually recursive classes --------- Co-authored-by: Carl Meyer <carl@astral.sh>
1 parent 656fc33 commit 33030b3

File tree

12 files changed

+1088
-95
lines changed

12 files changed

+1088
-95
lines changed

crates/ty_python_semantic/resources/mdtest/generics/pep695/classes.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,10 +237,15 @@ If the type of a constructor parameter is a class typevar, we can use that to in
237237
parameter. The types inferred from a type context and from a constructor parameter must be
238238
consistent with each other.
239239

240+
We have to add `x: T` to the classes to ensure they're not bivariant in `T` (__new__ and __init__
241+
signatures don't count towards variance).
242+
240243
### `__new__` only
241244

242245
```py
243246
class C[T]:
247+
x: T
248+
244249
def __new__(cls, x: T) -> "C[T]":
245250
return object.__new__(cls)
246251

@@ -254,6 +259,8 @@ wrong_innards: C[int] = C("five")
254259

255260
```py
256261
class C[T]:
262+
x: T
263+
257264
def __init__(self, x: T) -> None: ...
258265

259266
reveal_type(C(1)) # revealed: C[int]
@@ -266,6 +273,8 @@ wrong_innards: C[int] = C("five")
266273

267274
```py
268275
class C[T]:
276+
x: T
277+
269278
def __new__(cls, x: T) -> "C[T]":
270279
return object.__new__(cls)
271280

@@ -281,6 +290,8 @@ wrong_innards: C[int] = C("five")
281290

282291
```py
283292
class C[T]:
293+
x: T
294+
284295
def __new__(cls, *args, **kwargs) -> "C[T]":
285296
return object.__new__(cls)
286297

@@ -292,6 +303,8 @@ reveal_type(C(1)) # revealed: C[int]
292303
wrong_innards: C[int] = C("five")
293304

294305
class D[T]:
306+
x: T
307+
295308
def __new__(cls, x: T) -> "D[T]":
296309
return object.__new__(cls)
297310

@@ -378,6 +391,8 @@ def func8(t1: tuple[complex, list[int]], t2: tuple[int, *tuple[str, ...]], t3: t
378391

379392
```py
380393
class C[T]:
394+
x: T
395+
381396
def __init__[S](self, x: T, y: S) -> None: ...
382397

383398
reveal_type(C(1, 1)) # revealed: C[int]
@@ -395,6 +410,10 @@ from __future__ import annotations
395410
from typing import overload
396411

397412
class C[T]:
413+
# we need to use the type variable or else the class is bivariant in T, and
414+
# specializations become meaningless
415+
x: T
416+
398417
@overload
399418
def __init__(self: C[str], x: str) -> None: ...
400419
@overload

0 commit comments

Comments
 (0)