Skip to content

Commit daabee6

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

File tree

5 files changed

+12
-87
lines changed

5 files changed

+12
-87
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: 1 addition & 26 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

@@ -8056,7 +8031,7 @@ impl<'db> TypeVarBoundOrConstraints<'db> {
80568031
}
80578032
}
80588033

8059-
fn mark_typevars_inferable<'a>(
8034+
fn mark_typevars_inferable(
80608035
self,
80618036
db: &'db dyn Db,
80628037
visitor: &ApplyTypeMappingVisitor<'db>,

crates/ty_python_semantic/src/types/generics.rs

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -491,18 +491,6 @@ fn is_subtype_in_invariant_position<'db>(
491491
let base_bottom = base_type.bottom_materialization(db);
492492

493493
let is_subtype_of = |derived: Type<'db>, base: Type<'db>| {
494-
// TODO:
495-
// This should be removed and properly handled in the respective
496-
// `(Type::TypeVar(_), _) | (_, Type::TypeVar(_))` branch of
497-
// `Type::has_relation_to_impl`. Right now, we can not generally
498-
// return `ConstraintSet::from(true)` from that branch, as that
499-
// leads to union simplification, which means that we lose track
500-
// of type variables without recording the constraints under which
501-
// the relation holds.
502-
if matches!(base, Type::TypeVar(_)) || matches!(derived, Type::TypeVar(_)) {
503-
return ConstraintSet::from(true);
504-
}
505-
506494
derived.has_relation_to_impl(db, base, TypeRelation::Subtyping, visitor)
507495
};
508496
match (derived_materialization, base_materialization) {
@@ -1116,7 +1104,14 @@ impl<'db> SpecializationBuilder<'db> {
11161104
(Type::TypeVar(bound_typevar), ty) | (ty, Type::TypeVar(bound_typevar)) => {
11171105
match bound_typevar.typevar(self.db).bound_or_constraints(self.db) {
11181106
Some(TypeVarBoundOrConstraints::UpperBound(bound)) => {
1119-
if !ty.is_assignable_to(self.db, bound) {
1107+
let skip_assignability_check =
1108+
if bound_typevar.typevar(self.db).is_self(self.db) {
1109+
self.infer(ty, bound).is_ok()
1110+
} else {
1111+
false
1112+
};
1113+
1114+
if !(skip_assignability_check || ty.is_assignable_to(self.db, bound)) {
11201115
return Err(SpecializationError::MismatchedBound {
11211116
bound_typevar,
11221117
argument: ty,
@@ -1137,7 +1132,9 @@ impl<'db> SpecializationBuilder<'db> {
11371132
});
11381133
}
11391134
_ => {
1140-
self.add_type_mapping(bound_typevar, ty);
1135+
if !matches!(ty, Type::TypeVar(_)) {
1136+
self.add_type_mapping(bound_typevar, ty);
1137+
}
11411138
}
11421139
}
11431140
}

0 commit comments

Comments
 (0)