mirror of
https://github.com/astral-sh/ruff.git
synced 2025-11-03 05:13:00 +00:00
[ty] Fix subtyping of invariant generics specialized with Any (#20650)
This commit is contained in:
parent
d9473a2fcf
commit
20eb5b5b35
2 changed files with 51 additions and 11 deletions
|
|
@ -830,6 +830,42 @@ static_assert(not is_subtype_of(object, Any))
|
||||||
static_assert(is_subtype_of(int, Any | int))
|
static_assert(is_subtype_of(int, Any | int))
|
||||||
static_assert(is_subtype_of(Intersection[Any, int], int))
|
static_assert(is_subtype_of(Intersection[Any, int], int))
|
||||||
static_assert(not is_subtype_of(tuple[int, int], tuple[int, Any]))
|
static_assert(not is_subtype_of(tuple[int, int], tuple[int, Any]))
|
||||||
|
|
||||||
|
class Covariant[T]:
|
||||||
|
def get(self) -> T:
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
static_assert(not is_subtype_of(Covariant[Any], Covariant[Any]))
|
||||||
|
static_assert(not is_subtype_of(Covariant[Any], Covariant[int]))
|
||||||
|
static_assert(not is_subtype_of(Covariant[int], Covariant[Any]))
|
||||||
|
static_assert(is_subtype_of(Covariant[Any], Covariant[object]))
|
||||||
|
static_assert(not is_subtype_of(Covariant[object], Covariant[Any]))
|
||||||
|
|
||||||
|
class Contravariant[T]:
|
||||||
|
def receive(self, input: T): ...
|
||||||
|
|
||||||
|
static_assert(not is_subtype_of(Contravariant[Any], Contravariant[Any]))
|
||||||
|
static_assert(not is_subtype_of(Contravariant[Any], Contravariant[int]))
|
||||||
|
static_assert(not is_subtype_of(Contravariant[int], Contravariant[Any]))
|
||||||
|
static_assert(not is_subtype_of(Contravariant[Any], Contravariant[object]))
|
||||||
|
static_assert(is_subtype_of(Contravariant[object], Contravariant[Any]))
|
||||||
|
|
||||||
|
class Invariant[T]:
|
||||||
|
mutable_attribute: T
|
||||||
|
|
||||||
|
static_assert(not is_subtype_of(Invariant[Any], Invariant[Any]))
|
||||||
|
static_assert(not is_subtype_of(Invariant[Any], Invariant[int]))
|
||||||
|
static_assert(not is_subtype_of(Invariant[int], Invariant[Any]))
|
||||||
|
static_assert(not is_subtype_of(Invariant[Any], Invariant[object]))
|
||||||
|
static_assert(not is_subtype_of(Invariant[object], Invariant[Any]))
|
||||||
|
|
||||||
|
class Bivariant[T]: ...
|
||||||
|
|
||||||
|
static_assert(is_subtype_of(Bivariant[Any], Bivariant[Any]))
|
||||||
|
static_assert(is_subtype_of(Bivariant[Any], Bivariant[int]))
|
||||||
|
static_assert(is_subtype_of(Bivariant[int], Bivariant[Any]))
|
||||||
|
static_assert(is_subtype_of(Bivariant[Any], Bivariant[object]))
|
||||||
|
static_assert(is_subtype_of(Bivariant[object], Bivariant[Any]))
|
||||||
```
|
```
|
||||||
|
|
||||||
The same for `Unknown`:
|
The same for `Unknown`:
|
||||||
|
|
|
||||||
|
|
@ -602,18 +602,22 @@ fn has_relation_in_invariant_position<'db>(
|
||||||
base_mat,
|
base_mat,
|
||||||
visitor,
|
visitor,
|
||||||
),
|
),
|
||||||
// Subtyping between invariant type parameters without a top/bottom materialization involved
|
// Subtyping between invariant type parameters without a top/bottom materialization necessitates
|
||||||
// is equivalence
|
// checking the subtyping relation both ways: `A` must be a subtype of `B` *and* `B` must be a
|
||||||
(None, None, TypeRelation::Subtyping) => derived_type.when_equivalent_to(db, *base_type),
|
// subtype of `A`. The same applies to assignability.
|
||||||
(None, None, TypeRelation::Assignability) => derived_type
|
//
|
||||||
.has_relation_to_impl(db, *base_type, TypeRelation::Assignability, visitor)
|
// For subtyping between fully static types, this is the same as equivalence. However, we cannot
|
||||||
|
// use `is_equivalent_to` (or `when_equivalent_to`) here, because we (correctly) understand
|
||||||
|
// `list[Any]` as being equivalent to `list[Any]`, but we don't want `list[Any]` to be
|
||||||
|
// considered a subtype of `list[Any]`. For assignability, we would have the opposite issue if
|
||||||
|
// we simply checked for equivalence here: `Foo[Any]` should be considered assignable to
|
||||||
|
// `Foo[list[Any]]` even if `Foo` is invariant, and even though `Any` is not equivalent to
|
||||||
|
// `list[Any]`, because `Any` is assignable to `list[Any]` and `list[Any]` is assignable to
|
||||||
|
// `Any`.
|
||||||
|
(None, None, relation) => derived_type
|
||||||
|
.has_relation_to_impl(db, *base_type, relation, visitor)
|
||||||
.and(db, || {
|
.and(db, || {
|
||||||
base_type.has_relation_to_impl(
|
base_type.has_relation_to_impl(db, *derived_type, relation, visitor)
|
||||||
db,
|
|
||||||
*derived_type,
|
|
||||||
TypeRelation::Assignability,
|
|
||||||
visitor,
|
|
||||||
)
|
|
||||||
}),
|
}),
|
||||||
// For gradual types, A <: B (subtyping) is defined as Top[A] <: Bottom[B]
|
// For gradual types, A <: B (subtyping) is defined as Top[A] <: Bottom[B]
|
||||||
(None, Some(base_mat), TypeRelation::Subtyping) => is_subtype_in_invariant_position(
|
(None, Some(base_mat), TypeRelation::Subtyping) => is_subtype_in_invariant_position(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue