Skip to content

Commit 9793551

Browse files
authored
[ty] Create a specialization from a constraint set (#21414)
This patch lets us create specializations from a constraint set. The constraint encodes the restrictions on which types each typevar can specialize to. Given a generic context and a constraint set, we iterate through all of the generic context's typevars. For each typevar, we abstract the constraint set so that it only mentions the typevar in question (propagating derived facts if needed). We then find the "best representative type" for the typevar given the abstracted constraint set. When considering the BDD structure of the abstracted constraint set, each path from the BDD root to the `true` terminal represents one way that the constraint set can be satisfied. (This is also one of the clauses in the DNF representation of the constraint set's boolean formula.) Each of those paths is the conjunction of the individual constraints of each internal node that we traverse as we walk that path, giving a single lower/upper bound for the path. We use the upper bound as the "best" (i.e. "closest to `object`") type for that path. If there are multiple paths in the BDD, they technically represent independent possible specializations. If there's a single specialization that satisfies all of them, we will return that as the specialization. If not, then the constraint set is ambiguous. (This happens most often with constrained typevars.) We could in the future turn _each_ of the paths into separate specializations, but it's not clear what we would do with that, so instead we just report the ambiguity as a specialization failure.
1 parent 68ebd51 commit 9793551

File tree

15 files changed

+964
-170
lines changed

15 files changed

+964
-170
lines changed

crates/ty_python_semantic/resources/mdtest/annotations/self.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -503,9 +503,11 @@ class C[T]():
503503
def f(self: Self):
504504
def b(x: Self):
505505
reveal_type(x) # revealed: Self@f
506-
reveal_type(generic_context(b)) # revealed: None
506+
# revealed: None
507+
reveal_type(generic_context(b))
507508

508-
reveal_type(generic_context(C.f)) # revealed: tuple[Self@f]
509+
# revealed: ty_extensions.GenericContext[Self@f]
510+
reveal_type(generic_context(C.f))
509511
```
510512

511513
Even if the `Self` annotation appears first in the nested function, it is the method that binds
@@ -519,9 +521,11 @@ class C:
519521
def f(self: "C"):
520522
def b(x: Self):
521523
reveal_type(x) # revealed: Self@f
522-
reveal_type(generic_context(b)) # revealed: None
524+
# revealed: None
525+
reveal_type(generic_context(b))
523526

524-
reveal_type(generic_context(C.f)) # revealed: None
527+
# revealed: None
528+
reveal_type(generic_context(C.f))
525529
```
526530

527531
## Non-positional first parameters

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

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,14 @@ class TypeVarAndParamSpec(Generic[P, T]): ...
2121
class SingleTypeVarTuple(Generic[Unpack[Ts]]): ...
2222
class TypeVarAndTypeVarTuple(Generic[T, Unpack[Ts]]): ...
2323

24-
# revealed: tuple[T@SingleTypevar]
24+
# revealed: ty_extensions.GenericContext[T@SingleTypevar]
2525
reveal_type(generic_context(SingleTypevar))
26-
# revealed: tuple[T@MultipleTypevars, S@MultipleTypevars]
26+
# revealed: ty_extensions.GenericContext[T@MultipleTypevars, S@MultipleTypevars]
2727
reveal_type(generic_context(MultipleTypevars))
2828

29-
# revealed: tuple[P@SingleParamSpec]
29+
# revealed: ty_extensions.GenericContext[P@SingleParamSpec]
3030
reveal_type(generic_context(SingleParamSpec))
31-
# revealed: tuple[P@TypeVarAndParamSpec, T@TypeVarAndParamSpec]
31+
# revealed: ty_extensions.GenericContext[P@TypeVarAndParamSpec, T@TypeVarAndParamSpec]
3232
reveal_type(generic_context(TypeVarAndParamSpec))
3333

3434
# TODO: support `TypeVarTuple` properly (these should not reveal `None`)
@@ -66,9 +66,9 @@ class InheritedGeneric(MultipleTypevars[T, S]): ...
6666
class InheritedGenericPartiallySpecialized(MultipleTypevars[T, int]): ...
6767
class InheritedGenericFullySpecialized(MultipleTypevars[str, int]): ...
6868

69-
# revealed: tuple[T@InheritedGeneric, S@InheritedGeneric]
69+
# revealed: ty_extensions.GenericContext[T@InheritedGeneric, S@InheritedGeneric]
7070
reveal_type(generic_context(InheritedGeneric))
71-
# revealed: tuple[T@InheritedGenericPartiallySpecialized]
71+
# revealed: ty_extensions.GenericContext[T@InheritedGenericPartiallySpecialized]
7272
reveal_type(generic_context(InheritedGenericPartiallySpecialized))
7373
# revealed: None
7474
reveal_type(generic_context(InheritedGenericFullySpecialized))
@@ -90,7 +90,7 @@ class OuterClass(Generic[T]):
9090
# revealed: None
9191
reveal_type(generic_context(InnerClassInMethod))
9292

93-
# revealed: tuple[T@OuterClass]
93+
# revealed: ty_extensions.GenericContext[T@OuterClass]
9494
reveal_type(generic_context(OuterClass))
9595
```
9696

@@ -118,11 +118,11 @@ class ExplicitInheritedGenericPartiallySpecializedExtraTypevar(MultipleTypevars[
118118
# error: [invalid-generic-class] "`Generic` base class must include all type variables used in other base classes"
119119
class ExplicitInheritedGenericPartiallySpecializedMissingTypevar(MultipleTypevars[T, int], Generic[S]): ...
120120

121-
# revealed: tuple[T@ExplicitInheritedGeneric, S@ExplicitInheritedGeneric]
121+
# revealed: ty_extensions.GenericContext[T@ExplicitInheritedGeneric, S@ExplicitInheritedGeneric]
122122
reveal_type(generic_context(ExplicitInheritedGeneric))
123-
# revealed: tuple[T@ExplicitInheritedGenericPartiallySpecialized]
123+
# revealed: ty_extensions.GenericContext[T@ExplicitInheritedGenericPartiallySpecialized]
124124
reveal_type(generic_context(ExplicitInheritedGenericPartiallySpecialized))
125-
# revealed: tuple[T@ExplicitInheritedGenericPartiallySpecializedExtraTypevar, S@ExplicitInheritedGenericPartiallySpecializedExtraTypevar]
125+
# revealed: ty_extensions.GenericContext[T@ExplicitInheritedGenericPartiallySpecializedExtraTypevar, S@ExplicitInheritedGenericPartiallySpecializedExtraTypevar]
126126
reveal_type(generic_context(ExplicitInheritedGenericPartiallySpecializedExtraTypevar))
127127
```
128128

@@ -594,18 +594,27 @@ class C(Generic[T]):
594594
def generic_method(self, t: T, u: U) -> U:
595595
return u
596596

597-
reveal_type(generic_context(C)) # revealed: tuple[T@C]
598-
reveal_type(generic_context(C.method)) # revealed: tuple[Self@method]
599-
reveal_type(generic_context(C.generic_method)) # revealed: tuple[Self@generic_method, U@generic_method]
600-
reveal_type(generic_context(C[int])) # revealed: None
601-
reveal_type(generic_context(C[int].method)) # revealed: tuple[Self@method]
602-
reveal_type(generic_context(C[int].generic_method)) # revealed: tuple[Self@generic_method, U@generic_method]
597+
# revealed: ty_extensions.GenericContext[T@C]
598+
reveal_type(generic_context(C))
599+
# revealed: ty_extensions.GenericContext[Self@method]
600+
reveal_type(generic_context(C.method))
601+
# revealed: ty_extensions.GenericContext[Self@generic_method, U@generic_method]
602+
reveal_type(generic_context(C.generic_method))
603+
# revealed: None
604+
reveal_type(generic_context(C[int]))
605+
# revealed: ty_extensions.GenericContext[Self@method]
606+
reveal_type(generic_context(C[int].method))
607+
# revealed: ty_extensions.GenericContext[Self@generic_method, U@generic_method]
608+
reveal_type(generic_context(C[int].generic_method))
603609

604610
c: C[int] = C[int]()
605611
reveal_type(c.generic_method(1, "string")) # revealed: Literal["string"]
606-
reveal_type(generic_context(c)) # revealed: None
607-
reveal_type(generic_context(c.method)) # revealed: tuple[Self@method]
608-
reveal_type(generic_context(c.generic_method)) # revealed: tuple[Self@generic_method, U@generic_method]
612+
# revealed: None
613+
reveal_type(generic_context(c))
614+
# revealed: ty_extensions.GenericContext[Self@method]
615+
reveal_type(generic_context(c.method))
616+
# revealed: ty_extensions.GenericContext[Self@generic_method, U@generic_method]
617+
reveal_type(generic_context(c.generic_method))
609618
```
610619

611620
## Specializations propagate

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

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,21 @@ type TypeVarAndParamSpec[T, **P] = ...
2020
type SingleTypeVarTuple[*Ts] = ...
2121
type TypeVarAndTypeVarTuple[T, *Ts] = ...
2222

23-
# revealed: tuple[T@SingleTypevar]
23+
# revealed: ty_extensions.GenericContext[T@SingleTypevar]
2424
reveal_type(generic_context(SingleTypevar))
25-
# revealed: tuple[T@MultipleTypevars, S@MultipleTypevars]
25+
# revealed: ty_extensions.GenericContext[T@MultipleTypevars, S@MultipleTypevars]
2626
reveal_type(generic_context(MultipleTypevars))
2727

2828
# TODO: support `ParamSpec`/`TypeVarTuple` properly
2929
# (these should include the `ParamSpec`s and `TypeVarTuple`s in their generic contexts)
30-
reveal_type(generic_context(SingleParamSpec)) # revealed: tuple[()]
31-
reveal_type(generic_context(TypeVarAndParamSpec)) # revealed: tuple[T@TypeVarAndParamSpec]
32-
reveal_type(generic_context(SingleTypeVarTuple)) # revealed: tuple[()]
33-
reveal_type(generic_context(TypeVarAndTypeVarTuple)) # revealed: tuple[T@TypeVarAndTypeVarTuple]
30+
# revealed: ty_extensions.GenericContext[]
31+
reveal_type(generic_context(SingleParamSpec))
32+
# revealed: ty_extensions.GenericContext[T@TypeVarAndParamSpec]
33+
reveal_type(generic_context(TypeVarAndParamSpec))
34+
# revealed: ty_extensions.GenericContext[]
35+
reveal_type(generic_context(SingleTypeVarTuple))
36+
# revealed: ty_extensions.GenericContext[T@TypeVarAndTypeVarTuple]
37+
reveal_type(generic_context(TypeVarAndTypeVarTuple))
3438
```
3539

3640
You cannot use the same typevar more than once.

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

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,21 @@ class TypeVarAndParamSpec[T, **P]: ...
2020
class SingleTypeVarTuple[*Ts]: ...
2121
class TypeVarAndTypeVarTuple[T, *Ts]: ...
2222

23-
# revealed: tuple[T@SingleTypevar]
23+
# revealed: ty_extensions.GenericContext[T@SingleTypevar]
2424
reveal_type(generic_context(SingleTypevar))
25-
# revealed: tuple[T@MultipleTypevars, S@MultipleTypevars]
25+
# revealed: ty_extensions.GenericContext[T@MultipleTypevars, S@MultipleTypevars]
2626
reveal_type(generic_context(MultipleTypevars))
2727

2828
# TODO: support `ParamSpec`/`TypeVarTuple` properly
2929
# (these should include the `ParamSpec`s and `TypeVarTuple`s in their generic contexts)
30-
reveal_type(generic_context(SingleParamSpec)) # revealed: tuple[()]
31-
reveal_type(generic_context(TypeVarAndParamSpec)) # revealed: tuple[T@TypeVarAndParamSpec]
32-
reveal_type(generic_context(SingleTypeVarTuple)) # revealed: tuple[()]
33-
reveal_type(generic_context(TypeVarAndTypeVarTuple)) # revealed: tuple[T@TypeVarAndTypeVarTuple]
30+
# revealed: ty_extensions.GenericContext[]
31+
reveal_type(generic_context(SingleParamSpec))
32+
# revealed: ty_extensions.GenericContext[T@TypeVarAndParamSpec]
33+
reveal_type(generic_context(TypeVarAndParamSpec))
34+
# revealed: ty_extensions.GenericContext[]
35+
reveal_type(generic_context(SingleTypeVarTuple))
36+
# revealed: ty_extensions.GenericContext[T@TypeVarAndTypeVarTuple]
37+
reveal_type(generic_context(TypeVarAndTypeVarTuple))
3438
```
3539

3640
You cannot use the same typevar more than once.
@@ -49,9 +53,9 @@ class InheritedGeneric[U, V](MultipleTypevars[U, V]): ...
4953
class InheritedGenericPartiallySpecialized[U](MultipleTypevars[U, int]): ...
5054
class InheritedGenericFullySpecialized(MultipleTypevars[str, int]): ...
5155

52-
# revealed: tuple[U@InheritedGeneric, V@InheritedGeneric]
56+
# revealed: ty_extensions.GenericContext[U@InheritedGeneric, V@InheritedGeneric]
5357
reveal_type(generic_context(InheritedGeneric))
54-
# revealed: tuple[U@InheritedGenericPartiallySpecialized]
58+
# revealed: ty_extensions.GenericContext[U@InheritedGenericPartiallySpecialized]
5559
reveal_type(generic_context(InheritedGenericPartiallySpecialized))
5660
# revealed: None
5761
reveal_type(generic_context(InheritedGenericFullySpecialized))
@@ -64,7 +68,8 @@ the inheriting class generic.
6468
```py
6569
class InheritedGenericDefaultSpecialization(MultipleTypevars): ...
6670

67-
reveal_type(generic_context(InheritedGenericDefaultSpecialization)) # revealed: None
71+
# revealed: None
72+
reveal_type(generic_context(InheritedGenericDefaultSpecialization))
6873
```
6974

7075
You cannot use PEP-695 syntax and the legacy syntax in the same class definition.
@@ -512,18 +517,27 @@ class C[T]:
512517
# TODO: error
513518
def cannot_shadow_class_typevar[T](self, t: T): ...
514519

515-
reveal_type(generic_context(C)) # revealed: tuple[T@C]
516-
reveal_type(generic_context(C.method)) # revealed: tuple[Self@method]
517-
reveal_type(generic_context(C.generic_method)) # revealed: tuple[Self@generic_method, U@generic_method]
518-
reveal_type(generic_context(C[int])) # revealed: None
519-
reveal_type(generic_context(C[int].method)) # revealed: tuple[Self@method]
520-
reveal_type(generic_context(C[int].generic_method)) # revealed: tuple[Self@generic_method, U@generic_method]
520+
# revealed: ty_extensions.GenericContext[T@C]
521+
reveal_type(generic_context(C))
522+
# revealed: ty_extensions.GenericContext[Self@method]
523+
reveal_type(generic_context(C.method))
524+
# revealed: ty_extensions.GenericContext[Self@generic_method, U@generic_method]
525+
reveal_type(generic_context(C.generic_method))
526+
# revealed: None
527+
reveal_type(generic_context(C[int]))
528+
# revealed: ty_extensions.GenericContext[Self@method]
529+
reveal_type(generic_context(C[int].method))
530+
# revealed: ty_extensions.GenericContext[Self@generic_method, U@generic_method]
531+
reveal_type(generic_context(C[int].generic_method))
521532

522533
c: C[int] = C[int]()
523534
reveal_type(c.generic_method(1, "string")) # revealed: Literal["string"]
524-
reveal_type(generic_context(c)) # revealed: None
525-
reveal_type(generic_context(c.method)) # revealed: tuple[Self@method]
526-
reveal_type(generic_context(c.generic_method)) # revealed: tuple[Self@generic_method, U@generic_method]
535+
# revealed: None
536+
reveal_type(generic_context(c))
537+
# revealed: ty_extensions.GenericContext[Self@method]
538+
reveal_type(generic_context(c.method))
539+
# revealed: ty_extensions.GenericContext[Self@generic_method, U@generic_method]
540+
reveal_type(generic_context(c.generic_method))
527541
```
528542

529543
## Specializations propagate

crates/ty_python_semantic/resources/mdtest/generics/scoping.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,10 @@ from ty_extensions import generic_context
154154

155155
legacy.m("string", None) # error: [invalid-argument-type]
156156
reveal_type(legacy.m) # revealed: bound method Legacy[int].m[S](x: int, y: S@m) -> S@m
157-
reveal_type(generic_context(Legacy)) # revealed: tuple[T@Legacy]
158-
reveal_type(generic_context(legacy.m)) # revealed: tuple[Self@m, S@m]
157+
# revealed: ty_extensions.GenericContext[T@Legacy]
158+
reveal_type(generic_context(Legacy))
159+
# revealed: ty_extensions.GenericContext[Self@m, S@m]
160+
reveal_type(generic_context(legacy.m))
159161
```
160162

161163
With PEP 695 syntax, it is clearer that the method uses a separate typevar:

0 commit comments

Comments
 (0)