Skip to content

Commit 1c8bf05

Browse files
committed
Use generics solver to induct into the bounds of Self
1 parent a8cc55b commit 1c8bf05

File tree

5 files changed

+19
-77
lines changed

5 files changed

+19
-77
lines changed

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

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -366,31 +366,6 @@ reveal_type(f(g("a"))) # revealed: tuple[Literal["a"] | None, int]
366366
reveal_type(g(f("a"))) # revealed: tuple[Literal["a"], int] | None
367367
```
368368

369-
## Passing generic functions to generics functions
370-
371-
```py
372-
from typing import Callable, TypeVar
373-
374-
A = TypeVar("A")
375-
B = TypeVar("B")
376-
T = TypeVar("T")
377-
378-
def invoke(fn: Callable[[A], B], value: A) -> B:
379-
return fn(value)
380-
381-
def identity(x: T) -> T:
382-
return x
383-
384-
def head(xs: list[T]) -> T:
385-
return xs[0]
386-
387-
# TODO: this should be `Literal[1]`
388-
reveal_type(invoke(identity, 1)) # revealed: Unknown
389-
390-
# TODO: this should be `Unknown | int`
391-
reveal_type(invoke(head, [1, 2, 3])) # revealed: Unknown
392-
```
393-
394369
## Opaque decorators don't affect typevar binding
395370

396371
Inside the body of a generic function, we should be able to see that the typevars bound by that

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

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -323,27 +323,6 @@ reveal_type(f(g("a"))) # revealed: tuple[Literal["a"] | None, int]
323323
reveal_type(g(f("a"))) # revealed: tuple[Literal["a"], int] | None
324324
```
325325

326-
## Passing generic functions to generics functions
327-
328-
```py
329-
from typing import Callable
330-
331-
def invoke[A, B](fn: Callable[[A], B], value: A) -> B:
332-
return fn(value)
333-
334-
def identity[T](x: T) -> T:
335-
return x
336-
337-
def head[T](xs: list[T]) -> T:
338-
return xs[0]
339-
340-
# TODO: this should be `Literal[1]`
341-
reveal_type(invoke(identity, 1)) # revealed: Unknown
342-
343-
# TODO: this should be `Unknown | int`
344-
reveal_type(invoke(head, [1, 2, 3])) # revealed: Unknown
345-
```
346-
347326
## Protocols as TypeVar bounds
348327

349328
Protocol types can be used as TypeVar bounds, just like nominal types.

crates/ty_python_semantic/resources/mdtest/narrow/isinstance.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,6 @@ class Contravariant[T]:
342342
def _(x: object):
343343
if isinstance(x, Contravariant):
344344
reveal_type(x) # revealed: Contravariant[Never]
345-
# error: [invalid-argument-type] "Argument to bound method `push` is incorrect: Argument type `Contravariant[Never]` does not satisfy upper bound `Contravariant[T@Contravariant]` of type variable `Self`"
346345
# error: [invalid-argument-type] "Argument to bound method `push` is incorrect: Expected `Never`, found `Literal[42]`"
347346
x.push(42)
348347
```

crates/ty_python_semantic/src/types.rs

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1631,31 +1631,6 @@ impl<'db> Type<'db> {
16311631
// be specialized to `Never`.)
16321632
(_, Type::NonInferableTypeVar(_)) => ConstraintSet::from(false),
16331633

1634-
(Type::TypeVar(_), _) if relation.is_assignability() => {
1635-
// The implicit lower bound of a typevar is `Never`, which means
1636-
// that it is always assignable to any other type.
1637-
1638-
// TODO: record the unification constraints
1639-
1640-
ConstraintSet::from(true)
1641-
}
1642-
1643-
(_, Type::TypeVar(typevar))
1644-
if relation.is_assignability()
1645-
&& typevar.typevar(db).upper_bound(db).is_none_or(|bound| {
1646-
!self
1647-
.has_relation_to_impl(db, bound, relation, visitor)
1648-
.is_never_satisfied()
1649-
}) =>
1650-
{
1651-
// TODO: record the unification constraints
1652-
1653-
typevar
1654-
.typevar(db)
1655-
.upper_bound(db)
1656-
.when_none_or(|bound| self.has_relation_to_impl(db, bound, relation, visitor))
1657-
}
1658-
16591634
// TODO: Infer specializations here
16601635
(Type::TypeVar(_), _) | (_, Type::TypeVar(_)) => ConstraintSet::from(false),
16611636

crates/ty_python_semantic/src/types/generics.rs

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -499,9 +499,9 @@ fn is_subtype_in_invariant_position<'db>(
499499
// leads to union simplification, which means that we lose track
500500
// of type variables without recording the constraints under which
501501
// the relation holds.
502-
if matches!(base, Type::TypeVar(_)) || matches!(derived, Type::TypeVar(_)) {
503-
return ConstraintSet::from(true);
504-
}
502+
// if matches!(base, Type::TypeVar(_)) || matches!(derived, Type::TypeVar(_)) {
503+
// return ConstraintSet::from(true);
504+
// }
505505

506506
derived.has_relation_to_impl(db, base, TypeRelation::Subtyping, visitor)
507507
};
@@ -1039,6 +1039,11 @@ impl<'db> SpecializationBuilder<'db> {
10391039
}
10401040

10411041
fn add_type_mapping(&mut self, bound_typevar: BoundTypeVarInstance<'db>, ty: Type<'db>) {
1042+
dbg!(
1043+
bound_typevar.typevar(self.db).name(self.db),
1044+
&ty.display(self.db)
1045+
);
1046+
10421047
self.types
10431048
.entry(bound_typevar)
10441049
.and_modify(|existing| {
@@ -1116,7 +1121,14 @@ impl<'db> SpecializationBuilder<'db> {
11161121
(Type::TypeVar(bound_typevar), ty) | (ty, Type::TypeVar(bound_typevar)) => {
11171122
match bound_typevar.typevar(self.db).bound_or_constraints(self.db) {
11181123
Some(TypeVarBoundOrConstraints::UpperBound(bound)) => {
1119-
if !ty.is_assignable_to(self.db, bound) {
1124+
let skip_assignability_check =
1125+
if bound_typevar.typevar(self.db).is_self(self.db) {
1126+
self.infer(ty, bound).is_ok()
1127+
} else {
1128+
false
1129+
};
1130+
1131+
if !(skip_assignability_check || ty.is_assignable_to(self.db, bound)) {
11201132
return Err(SpecializationError::MismatchedBound {
11211133
bound_typevar,
11221134
argument: ty,
@@ -1137,7 +1149,9 @@ impl<'db> SpecializationBuilder<'db> {
11371149
});
11381150
}
11391151
_ => {
1140-
self.add_type_mapping(bound_typevar, ty);
1152+
if !matches!(ty, Type::TypeVar(_)) {
1153+
self.add_type_mapping(bound_typevar, ty);
1154+
}
11411155
}
11421156
}
11431157
}

0 commit comments

Comments
 (0)