mirror of https://github.com/astral-sh/ruff
wip
This commit is contained in:
parent
5b547171bc
commit
1a73410486
|
|
@ -315,10 +315,11 @@ static_assert(is_equivalent_to(C[A], C[A]))
|
||||||
static_assert(is_equivalent_to(C[B], C[B]))
|
static_assert(is_equivalent_to(C[B], C[B]))
|
||||||
static_assert(is_equivalent_to(C[B], C[A]))
|
static_assert(is_equivalent_to(C[B], C[A]))
|
||||||
static_assert(is_equivalent_to(C[A], C[B]))
|
static_assert(is_equivalent_to(C[A], C[B]))
|
||||||
static_assert(is_equivalent_to(C[A], C[Any]))
|
|
||||||
static_assert(is_equivalent_to(C[B], C[Any]))
|
static_assert(not is_equivalent_to(C[A], C[Any]))
|
||||||
static_assert(is_equivalent_to(C[Any], C[A]))
|
static_assert(not is_equivalent_to(C[B], C[Any]))
|
||||||
static_assert(is_equivalent_to(C[Any], C[B]))
|
static_assert(not is_equivalent_to(C[Any], C[A]))
|
||||||
|
static_assert(not is_equivalent_to(C[Any], C[B]))
|
||||||
|
|
||||||
static_assert(not is_equivalent_to(D[A], C[A]))
|
static_assert(not is_equivalent_to(D[A], C[A]))
|
||||||
static_assert(not is_equivalent_to(D[B], C[B]))
|
static_assert(not is_equivalent_to(D[B], C[B]))
|
||||||
|
|
|
||||||
|
|
@ -1147,10 +1147,10 @@ static_assert(
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
static_assert(not is_equivalent_to(GenericProto[str], GenericProto[int]))
|
static_assert(not is_equivalent_to(GenericProto[str], GenericProto[int])) # error: [static-assert-error]
|
||||||
static_assert(not is_equivalent_to(GenericProto[str], LegacyGenericProto[int]))
|
static_assert(not is_equivalent_to(GenericProto[str], LegacyGenericProto[int])) # error: [static-assert-error]
|
||||||
static_assert(not is_equivalent_to(GenericProto, GenericProto[int]))
|
static_assert(not is_equivalent_to(GenericProto, GenericProto[int])) # error: [static-assert-error]
|
||||||
static_assert(not is_equivalent_to(LegacyGenericProto, LegacyGenericProto[int]))
|
static_assert(not is_equivalent_to(LegacyGenericProto, LegacyGenericProto[int])) # error: [static-assert-error]
|
||||||
```
|
```
|
||||||
|
|
||||||
## Intersections of protocols
|
## Intersections of protocols
|
||||||
|
|
@ -1543,8 +1543,7 @@ from ty_extensions import is_equivalent_to
|
||||||
class HasMutableXAttr(Protocol):
|
class HasMutableXAttr(Protocol):
|
||||||
x: int
|
x: int
|
||||||
|
|
||||||
# TODO: should pass
|
static_assert(is_equivalent_to(HasMutableXAttr, HasMutableXProperty))
|
||||||
static_assert(is_equivalent_to(HasMutableXAttr, HasMutableXProperty)) # error: [static-assert-error]
|
|
||||||
|
|
||||||
static_assert(is_subtype_of(HasMutableXAttr, HasXProperty))
|
static_assert(is_subtype_of(HasMutableXAttr, HasXProperty))
|
||||||
static_assert(is_assignable_to(HasMutableXAttr, HasXProperty))
|
static_assert(is_assignable_to(HasMutableXAttr, HasXProperty))
|
||||||
|
|
@ -1835,9 +1834,7 @@ class P4(Protocol):
|
||||||
def z(self, value: int) -> None: ...
|
def z(self, value: int) -> None: ...
|
||||||
|
|
||||||
static_assert(is_equivalent_to(P1, P2))
|
static_assert(is_equivalent_to(P1, P2))
|
||||||
|
static_assert(is_equivalent_to(P3, P4))
|
||||||
# TODO: should pass
|
|
||||||
static_assert(is_equivalent_to(P3, P4)) # error: [static-assert-error]
|
|
||||||
```
|
```
|
||||||
|
|
||||||
As with protocols that only have non-method members, this also holds true when they appear in
|
As with protocols that only have non-method members, this also holds true when they appear in
|
||||||
|
|
@ -1848,9 +1845,7 @@ class A: ...
|
||||||
class B: ...
|
class B: ...
|
||||||
|
|
||||||
static_assert(is_equivalent_to(A | B | P1, P2 | B | A))
|
static_assert(is_equivalent_to(A | B | P1, P2 | B | A))
|
||||||
|
static_assert(is_equivalent_to(A | B | P3, P4 | B | A))
|
||||||
# TODO: should pass
|
|
||||||
static_assert(is_equivalent_to(A | B | P3, P4 | B | A)) # error: [static-assert-error]
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Narrowing of protocols
|
## Narrowing of protocols
|
||||||
|
|
@ -2131,8 +2126,6 @@ class Bar(Protocol):
|
||||||
@property
|
@property
|
||||||
def x(self) -> "Bar": ...
|
def x(self) -> "Bar": ...
|
||||||
|
|
||||||
# TODO: this should pass
|
|
||||||
# error: [static-assert-error]
|
|
||||||
static_assert(is_equivalent_to(Foo, Bar))
|
static_assert(is_equivalent_to(Foo, Bar))
|
||||||
|
|
||||||
T = TypeVar("T", bound="TypeVarRecursive")
|
T = TypeVar("T", bound="TypeVarRecursive")
|
||||||
|
|
|
||||||
|
|
@ -188,10 +188,6 @@ pub(crate) type HasRelationToVisitor<'db, C> =
|
||||||
pub(crate) type IsDisjointVisitor<'db, C> = PairVisitor<'db, IsDisjoint, C>;
|
pub(crate) type IsDisjointVisitor<'db, C> = PairVisitor<'db, IsDisjoint, C>;
|
||||||
pub(crate) struct IsDisjoint;
|
pub(crate) struct IsDisjoint;
|
||||||
|
|
||||||
/// A [`PairVisitor`] that is used in `is_equivalent` methods.
|
|
||||||
pub(crate) type IsEquivalentVisitor<'db, C> = PairVisitor<'db, IsEquivalent, C>;
|
|
||||||
pub(crate) struct IsEquivalent;
|
|
||||||
|
|
||||||
/// A [`CycleDetector`] that is used in `find_legacy_typevars` methods.
|
/// A [`CycleDetector`] that is used in `find_legacy_typevars` methods.
|
||||||
pub(crate) type FindLegacyTypeVarsVisitor<'db> = CycleDetector<FindLegacyTypeVars, Type<'db>, ()>;
|
pub(crate) type FindLegacyTypeVarsVisitor<'db> = CycleDetector<FindLegacyTypeVars, Type<'db>, ()>;
|
||||||
pub(crate) struct FindLegacyTypeVars;
|
pub(crate) struct FindLegacyTypeVars;
|
||||||
|
|
@ -1422,9 +1418,15 @@ impl<'db> Type<'db> {
|
||||||
// no other type is a subtype of or assignable to `Never`.
|
// no other type is a subtype of or assignable to `Never`.
|
||||||
(_, Type::Never) => C::unsatisfiable(db),
|
(_, Type::Never) => C::unsatisfiable(db),
|
||||||
|
|
||||||
(Type::Union(union), _) => union.elements(db).iter().when_all(db, |&elem_ty| {
|
(Type::Union(union), _) => union
|
||||||
elem_ty.has_relation_to_impl(db, target, relation, visitor)
|
.elements(db)
|
||||||
}),
|
.iter()
|
||||||
|
.when_all(db, |&elem_ty| {
|
||||||
|
elem_ty.has_relation_to_impl(db, target, relation, visitor)
|
||||||
|
})
|
||||||
|
.or(db, || {
|
||||||
|
C::from_bool(db, self.normalized(db) == target.normalized(db))
|
||||||
|
}),
|
||||||
|
|
||||||
(_, Type::Union(union)) => union.elements(db).iter().when_any(db, |&elem_ty| {
|
(_, Type::Union(union)) => union.elements(db).iter().when_any(db, |&elem_ty| {
|
||||||
self.has_relation_to_impl(db, elem_ty, relation, visitor)
|
self.has_relation_to_impl(db, elem_ty, relation, visitor)
|
||||||
|
|
@ -1465,7 +1467,7 @@ impl<'db> Type<'db> {
|
||||||
(left, Type::AlwaysTruthy) => C::from_bool(db, left.bool(db).is_always_true()),
|
(left, Type::AlwaysTruthy) => C::from_bool(db, left.bool(db).is_always_true()),
|
||||||
// Currently, the only supertype of `AlwaysFalsy` and `AlwaysTruthy` is the universal set (object instance).
|
// Currently, the only supertype of `AlwaysFalsy` and `AlwaysTruthy` is the universal set (object instance).
|
||||||
(Type::AlwaysFalsy | Type::AlwaysTruthy, _) => {
|
(Type::AlwaysFalsy | Type::AlwaysTruthy, _) => {
|
||||||
target.when_equivalent_to(db, Type::object(db))
|
C::from_bool(db, target.is_equivalent_to(db, Type::object(db)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// These clauses handle type variants that include function literals. A function
|
// These clauses handle type variants that include function literals. A function
|
||||||
|
|
@ -1602,13 +1604,16 @@ impl<'db> Type<'db> {
|
||||||
|
|
||||||
(Type::Callable(_), _) => C::unsatisfiable(db),
|
(Type::Callable(_), _) => C::unsatisfiable(db),
|
||||||
|
|
||||||
(Type::BoundSuper(_), Type::BoundSuper(_)) => self.when_equivalent_to(db, target),
|
(Type::BoundSuper(_), Type::BoundSuper(_)) => visitor
|
||||||
|
.visit((self, target, relation), || {
|
||||||
|
self.when_equivalent_to_impl(db, target, visitor)
|
||||||
|
}),
|
||||||
(Type::BoundSuper(_), _) => KnownClass::Super
|
(Type::BoundSuper(_), _) => KnownClass::Super
|
||||||
.to_instance(db)
|
.to_instance(db)
|
||||||
.has_relation_to_impl(db, target, relation, visitor),
|
.has_relation_to_impl(db, target, relation, visitor),
|
||||||
|
|
||||||
// `Literal[<class 'C'>]` is a subtype of `type[B]` if `C` is a subclass of `B`,
|
// `Literal[<class 'C'>]` is a subtype of `type[B]` if `C` is a subclass of `B`,
|
||||||
// since `type[B]` describes all possible runtime subclasses of the class object `B`.
|
// since `type[B]` describes all possible runtiexime subclasses of the class object `B`.
|
||||||
(Type::ClassLiteral(class), Type::SubclassOf(target_subclass_ty)) => target_subclass_ty
|
(Type::ClassLiteral(class), Type::SubclassOf(target_subclass_ty)) => target_subclass_ty
|
||||||
.subclass_of()
|
.subclass_of()
|
||||||
.into_class()
|
.into_class()
|
||||||
|
|
@ -1740,95 +1745,31 @@ impl<'db> Type<'db> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn when_equivalent_to<C: Constraints<'db>>(self, db: &'db dyn Db, other: Type<'db>) -> C {
|
fn when_equivalent_to<C: Constraints<'db>>(self, db: &'db dyn Db, other: Type<'db>) -> C {
|
||||||
self.is_equivalent_to_impl(
|
let visitor = HasRelationToVisitor::new(C::always_satisfiable(db));
|
||||||
db,
|
self.when_equivalent_to_impl(db, other, &visitor)
|
||||||
other,
|
|
||||||
&IsEquivalentVisitor::new(C::always_satisfiable(db)),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn is_equivalent_to_impl<C: Constraints<'db>>(
|
fn when_equivalent_to_impl<C: Constraints<'db>>(
|
||||||
self,
|
self,
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
other: Type<'db>,
|
other: Type<'db>,
|
||||||
visitor: &IsEquivalentVisitor<'db, C>,
|
visitor: &HasRelationToVisitor<'db, C>,
|
||||||
) -> C {
|
) -> C {
|
||||||
if self == other {
|
let self_relation = self.has_relation_to_impl(db, other, TypeRelation::Subtyping, visitor);
|
||||||
return C::always_satisfiable(db);
|
// println!(
|
||||||
}
|
// "{}, {}, {}",
|
||||||
|
// self.display(db),
|
||||||
match (self, other) {
|
// other.display(db),
|
||||||
(Type::Dynamic(_), Type::Dynamic(_)) => C::always_satisfiable(db),
|
// self_relation.display(db)
|
||||||
|
// );
|
||||||
(Type::SubclassOf(first), Type::SubclassOf(second)) => {
|
let other_relation = other.has_relation_to_impl(db, self, TypeRelation::Subtyping, visitor);
|
||||||
match (first.subclass_of(), second.subclass_of()) {
|
// println!(
|
||||||
(first, second) if first == second => C::always_satisfiable(db),
|
// "{}, {}, {}",
|
||||||
(SubclassOfInner::Dynamic(_), SubclassOfInner::Dynamic(_)) => {
|
// other.display(db),
|
||||||
C::always_satisfiable(db)
|
// self.display(db),
|
||||||
}
|
// other_relation.display(db)
|
||||||
_ => C::unsatisfiable(db),
|
// );
|
||||||
}
|
self_relation.and(db, || other_relation)
|
||||||
}
|
|
||||||
|
|
||||||
(Type::TypeAlias(self_alias), _) => {
|
|
||||||
let self_alias_ty = self_alias.value_type(db).normalized(db);
|
|
||||||
visitor.visit((self_alias_ty, other), || {
|
|
||||||
self_alias_ty.is_equivalent_to_impl(db, other, visitor)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
(_, Type::TypeAlias(other_alias)) => {
|
|
||||||
let other_alias_ty = other_alias.value_type(db).normalized(db);
|
|
||||||
visitor.visit((self, other_alias_ty), || {
|
|
||||||
self.is_equivalent_to_impl(db, other_alias_ty, visitor)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
(Type::NominalInstance(first), Type::NominalInstance(second)) => {
|
|
||||||
first.is_equivalent_to_impl(db, second, visitor)
|
|
||||||
}
|
|
||||||
|
|
||||||
(Type::Union(first), Type::Union(second)) => {
|
|
||||||
first.is_equivalent_to_impl(db, second, visitor)
|
|
||||||
}
|
|
||||||
|
|
||||||
(Type::Intersection(first), Type::Intersection(second)) => {
|
|
||||||
first.is_equivalent_to_impl(db, second, visitor)
|
|
||||||
}
|
|
||||||
|
|
||||||
(Type::FunctionLiteral(self_function), Type::FunctionLiteral(target_function)) => {
|
|
||||||
self_function.is_equivalent_to_impl(db, target_function, visitor)
|
|
||||||
}
|
|
||||||
(Type::BoundMethod(self_method), Type::BoundMethod(target_method)) => {
|
|
||||||
self_method.is_equivalent_to_impl(db, target_method, visitor)
|
|
||||||
}
|
|
||||||
(Type::MethodWrapper(self_method), Type::MethodWrapper(target_method)) => {
|
|
||||||
self_method.is_equivalent_to_impl(db, target_method, visitor)
|
|
||||||
}
|
|
||||||
(Type::Callable(first), Type::Callable(second)) => {
|
|
||||||
first.is_equivalent_to_impl(db, second, visitor)
|
|
||||||
}
|
|
||||||
|
|
||||||
(Type::ProtocolInstance(first), Type::ProtocolInstance(second)) => {
|
|
||||||
first.is_equivalent_to_impl(db, second, visitor)
|
|
||||||
}
|
|
||||||
(Type::ProtocolInstance(protocol), nominal @ Type::NominalInstance(n))
|
|
||||||
| (nominal @ Type::NominalInstance(n), Type::ProtocolInstance(protocol)) => {
|
|
||||||
C::from_bool(db, n.is_object(db) && protocol.normalized(db) == nominal)
|
|
||||||
}
|
|
||||||
// An instance of an enum class is equivalent to an enum literal of that class,
|
|
||||||
// if that enum has only has one member.
|
|
||||||
(Type::NominalInstance(instance), Type::EnumLiteral(literal))
|
|
||||||
| (Type::EnumLiteral(literal), Type::NominalInstance(instance)) => {
|
|
||||||
if literal.enum_class_instance(db) != Type::NominalInstance(instance) {
|
|
||||||
return C::unsatisfiable(db);
|
|
||||||
}
|
|
||||||
|
|
||||||
let class_literal = instance.class(db).class_literal(db).0;
|
|
||||||
C::from_bool(db, is_single_member_enum(db, class_literal))
|
|
||||||
}
|
|
||||||
_ => C::unsatisfiable(db),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return true if this type and `other` have no common elements.
|
/// Return true if this type and `other` have no common elements.
|
||||||
|
|
@ -8855,21 +8796,6 @@ impl<'db> BoundMethodType<'db> {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_equivalent_to_impl<C: Constraints<'db>>(
|
|
||||||
self,
|
|
||||||
db: &'db dyn Db,
|
|
||||||
other: Self,
|
|
||||||
visitor: &IsEquivalentVisitor<'db, C>,
|
|
||||||
) -> C {
|
|
||||||
self.function(db)
|
|
||||||
.is_equivalent_to_impl(db, other.function(db), visitor)
|
|
||||||
.and(db, || {
|
|
||||||
other
|
|
||||||
.self_instance(db)
|
|
||||||
.is_equivalent_to_impl(db, self.self_instance(db), visitor)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This type represents the set of all callable objects with a certain, possibly overloaded,
|
/// This type represents the set of all callable objects with a certain, possibly overloaded,
|
||||||
|
|
@ -9002,25 +8928,6 @@ impl<'db> CallableType<'db> {
|
||||||
self.signatures(db)
|
self.signatures(db)
|
||||||
.has_relation_to_impl(db, other.signatures(db), relation, visitor)
|
.has_relation_to_impl(db, other.signatures(db), relation, visitor)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check whether this callable type is equivalent to another callable type.
|
|
||||||
///
|
|
||||||
/// See [`Type::is_equivalent_to`] for more details.
|
|
||||||
fn is_equivalent_to_impl<C: Constraints<'db>>(
|
|
||||||
self,
|
|
||||||
db: &'db dyn Db,
|
|
||||||
other: Self,
|
|
||||||
visitor: &IsEquivalentVisitor<'db, C>,
|
|
||||||
) -> C {
|
|
||||||
if self == other {
|
|
||||||
return C::always_satisfiable(db);
|
|
||||||
}
|
|
||||||
|
|
||||||
C::from_bool(db, self.is_function_like(db) == other.is_function_like(db)).and(db, || {
|
|
||||||
self.signatures(db)
|
|
||||||
.is_equivalent_to_impl(db, other.signatures(db), visitor)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents a specific instance of `types.MethodWrapperType`
|
/// Represents a specific instance of `types.MethodWrapperType`
|
||||||
|
|
@ -9108,44 +9015,6 @@ impl<'db> MethodWrapperKind<'db> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_equivalent_to_impl<C: Constraints<'db>>(
|
|
||||||
self,
|
|
||||||
db: &'db dyn Db,
|
|
||||||
other: Self,
|
|
||||||
visitor: &IsEquivalentVisitor<'db, C>,
|
|
||||||
) -> C {
|
|
||||||
match (self, other) {
|
|
||||||
(
|
|
||||||
MethodWrapperKind::FunctionTypeDunderGet(self_function),
|
|
||||||
MethodWrapperKind::FunctionTypeDunderGet(other_function),
|
|
||||||
) => self_function.is_equivalent_to_impl(db, other_function, visitor),
|
|
||||||
|
|
||||||
(
|
|
||||||
MethodWrapperKind::FunctionTypeDunderCall(self_function),
|
|
||||||
MethodWrapperKind::FunctionTypeDunderCall(other_function),
|
|
||||||
) => self_function.is_equivalent_to_impl(db, other_function, visitor),
|
|
||||||
|
|
||||||
(MethodWrapperKind::PropertyDunderGet(_), MethodWrapperKind::PropertyDunderGet(_))
|
|
||||||
| (MethodWrapperKind::PropertyDunderSet(_), MethodWrapperKind::PropertyDunderSet(_))
|
|
||||||
| (MethodWrapperKind::StrStartswith(_), MethodWrapperKind::StrStartswith(_)) => {
|
|
||||||
C::from_bool(db, self == other)
|
|
||||||
}
|
|
||||||
|
|
||||||
(
|
|
||||||
MethodWrapperKind::FunctionTypeDunderGet(_)
|
|
||||||
| MethodWrapperKind::FunctionTypeDunderCall(_)
|
|
||||||
| MethodWrapperKind::PropertyDunderGet(_)
|
|
||||||
| MethodWrapperKind::PropertyDunderSet(_)
|
|
||||||
| MethodWrapperKind::StrStartswith(_),
|
|
||||||
MethodWrapperKind::FunctionTypeDunderGet(_)
|
|
||||||
| MethodWrapperKind::FunctionTypeDunderCall(_)
|
|
||||||
| MethodWrapperKind::PropertyDunderGet(_)
|
|
||||||
| MethodWrapperKind::PropertyDunderSet(_)
|
|
||||||
| MethodWrapperKind::StrStartswith(_),
|
|
||||||
) => C::unsatisfiable(db),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn normalized_impl(self, db: &'db dyn Db, visitor: &NormalizedVisitor<'db>) -> Self {
|
fn normalized_impl(self, db: &'db dyn Db, visitor: &NormalizedVisitor<'db>) -> Self {
|
||||||
match self {
|
match self {
|
||||||
MethodWrapperKind::FunctionTypeDunderGet(function) => {
|
MethodWrapperKind::FunctionTypeDunderGet(function) => {
|
||||||
|
|
@ -9640,14 +9509,6 @@ impl<'db> UnionType<'db> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new union type with the elements normalized.
|
|
||||||
///
|
|
||||||
/// See [`Type::normalized`] for more details.
|
|
||||||
#[must_use]
|
|
||||||
pub(crate) fn normalized(self, db: &'db dyn Db) -> Type<'db> {
|
|
||||||
self.normalized_impl(db, &NormalizedVisitor::default())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn normalized_impl(
|
pub(crate) fn normalized_impl(
|
||||||
self,
|
self,
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
|
|
@ -9664,32 +9525,6 @@ impl<'db> UnionType<'db> {
|
||||||
)
|
)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn is_equivalent_to_impl<C: Constraints<'db>>(
|
|
||||||
self,
|
|
||||||
db: &'db dyn Db,
|
|
||||||
other: Self,
|
|
||||||
_visitor: &IsEquivalentVisitor<'db, C>,
|
|
||||||
) -> C {
|
|
||||||
if self == other {
|
|
||||||
return C::always_satisfiable(db);
|
|
||||||
}
|
|
||||||
|
|
||||||
let self_elements = self.elements(db);
|
|
||||||
let other_elements = other.elements(db);
|
|
||||||
|
|
||||||
if self_elements.len() != other_elements.len() {
|
|
||||||
return C::unsatisfiable(db);
|
|
||||||
}
|
|
||||||
|
|
||||||
let sorted_self = self.normalized(db);
|
|
||||||
|
|
||||||
if sorted_self == Type::Union(other) {
|
|
||||||
return C::always_satisfiable(db);
|
|
||||||
}
|
|
||||||
|
|
||||||
C::from_bool(db, sorted_self == other.normalized(db))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[salsa::interned(debug, heap_size=IntersectionType::heap_size)]
|
#[salsa::interned(debug, heap_size=IntersectionType::heap_size)]
|
||||||
|
|
@ -9734,15 +9569,6 @@ impl<'db> IntersectionType<'db> {
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a new `IntersectionType` instance with the positive and negative types sorted
|
|
||||||
/// according to a canonical ordering, and other normalizations applied to each element as applicable.
|
|
||||||
///
|
|
||||||
/// See [`Type::normalized`] for more details.
|
|
||||||
#[must_use]
|
|
||||||
pub(crate) fn normalized(self, db: &'db dyn Db) -> Self {
|
|
||||||
self.normalized_impl(db, &NormalizedVisitor::default())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn normalized_impl(self, db: &'db dyn Db, visitor: &NormalizedVisitor<'db>) -> Self {
|
pub(crate) fn normalized_impl(self, db: &'db dyn Db, visitor: &NormalizedVisitor<'db>) -> Self {
|
||||||
fn normalized_set<'db>(
|
fn normalized_set<'db>(
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
|
|
@ -9765,40 +9591,6 @@ impl<'db> IntersectionType<'db> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return `true` if `self` represents exactly the same set of possible runtime objects as `other`
|
|
||||||
pub(crate) fn is_equivalent_to_impl<C: Constraints<'db>>(
|
|
||||||
self,
|
|
||||||
db: &'db dyn Db,
|
|
||||||
other: Self,
|
|
||||||
_visitor: &IsEquivalentVisitor<'db, C>,
|
|
||||||
) -> C {
|
|
||||||
if self == other {
|
|
||||||
return C::always_satisfiable(db);
|
|
||||||
}
|
|
||||||
|
|
||||||
let self_positive = self.positive(db);
|
|
||||||
let other_positive = other.positive(db);
|
|
||||||
|
|
||||||
if self_positive.len() != other_positive.len() {
|
|
||||||
return C::unsatisfiable(db);
|
|
||||||
}
|
|
||||||
|
|
||||||
let self_negative = self.negative(db);
|
|
||||||
let other_negative = other.negative(db);
|
|
||||||
|
|
||||||
if self_negative.len() != other_negative.len() {
|
|
||||||
return C::unsatisfiable(db);
|
|
||||||
}
|
|
||||||
|
|
||||||
let sorted_self = self.normalized(db);
|
|
||||||
|
|
||||||
if sorted_self == other {
|
|
||||||
return C::always_satisfiable(db);
|
|
||||||
}
|
|
||||||
|
|
||||||
C::from_bool(db, sorted_self == other.normalized(db))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns an iterator over the positive elements of the intersection. If
|
/// Returns an iterator over the positive elements of the intersection. If
|
||||||
/// there are no positive elements, returns a single `object` type.
|
/// there are no positive elements, returns a single `object` type.
|
||||||
fn positive_elements_or_object(self, db: &'db dyn Db) -> impl Iterator<Item = Type<'db>> {
|
fn positive_elements_or_object(self, db: &'db dyn Db) -> impl Iterator<Item = Type<'db>> {
|
||||||
|
|
|
||||||
|
|
@ -27,10 +27,10 @@ use crate::types::typed_dict::typed_dict_params_from_class_def;
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
ApplyTypeMappingVisitor, Binding, BoundSuperError, BoundSuperType, CallableType,
|
ApplyTypeMappingVisitor, Binding, BoundSuperError, BoundSuperType, CallableType,
|
||||||
DataclassParams, DeprecatedInstance, FindLegacyTypeVarsVisitor, HasRelationToVisitor,
|
DataclassParams, DeprecatedInstance, FindLegacyTypeVarsVisitor, HasRelationToVisitor,
|
||||||
IsEquivalentVisitor, KnownInstanceType, ManualPEP695TypeAliasType, MaterializationKind,
|
KnownInstanceType, ManualPEP695TypeAliasType, MaterializationKind, NormalizedVisitor,
|
||||||
NormalizedVisitor, PropertyInstanceType, StringLiteralType, TypeAliasType, TypeMapping,
|
PropertyInstanceType, StringLiteralType, TypeAliasType, TypeMapping, TypeRelation,
|
||||||
TypeRelation, TypeVarBoundOrConstraints, TypeVarInstance, TypeVarKind, TypedDictParams,
|
TypeVarBoundOrConstraints, TypeVarInstance, TypeVarKind, TypedDictParams, UnionBuilder,
|
||||||
UnionBuilder, VarianceInferable, declaration_type, infer_definition_types,
|
VarianceInferable, declaration_type, infer_definition_types,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
Db, FxIndexMap, FxOrderSet, Program,
|
Db, FxIndexMap, FxOrderSet, Program,
|
||||||
|
|
@ -584,33 +584,6 @@ impl<'db> ClassType<'db> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn is_equivalent_to_impl<C: Constraints<'db>>(
|
|
||||||
self,
|
|
||||||
db: &'db dyn Db,
|
|
||||||
other: ClassType<'db>,
|
|
||||||
visitor: &IsEquivalentVisitor<'db, C>,
|
|
||||||
) -> C {
|
|
||||||
if self == other {
|
|
||||||
return C::always_satisfiable(db);
|
|
||||||
}
|
|
||||||
|
|
||||||
match (self, other) {
|
|
||||||
// A non-generic class is never equivalent to a generic class.
|
|
||||||
// Two non-generic classes are only equivalent if they are equal (handled above).
|
|
||||||
(ClassType::NonGeneric(_), _) | (_, ClassType::NonGeneric(_)) => C::unsatisfiable(db),
|
|
||||||
|
|
||||||
(ClassType::Generic(this), ClassType::Generic(other)) => {
|
|
||||||
C::from_bool(db, this.origin(db) == other.origin(db)).and(db, || {
|
|
||||||
this.specialization(db).is_equivalent_to_impl(
|
|
||||||
db,
|
|
||||||
other.specialization(db),
|
|
||||||
visitor,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the metaclass of this class, or `type[Unknown]` if the metaclass cannot be inferred.
|
/// Return the metaclass of this class, or `type[Unknown]` if the metaclass cannot be inferred.
|
||||||
pub(super) fn metaclass(self, db: &'db dyn Db) -> Type<'db> {
|
pub(super) fn metaclass(self, db: &'db dyn Db) -> Type<'db> {
|
||||||
let (class_literal, specialization) = self.class_literal(db);
|
let (class_literal, specialization) = self.class_literal(db);
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ use crate::Db;
|
||||||
use crate::types::{BoundTypeVarInstance, IntersectionType, Type, UnionType};
|
use crate::types::{BoundTypeVarInstance, IntersectionType, Type, UnionType};
|
||||||
|
|
||||||
/// Encodes the constraints under which a type property (e.g. assignability) holds.
|
/// Encodes the constraints under which a type property (e.g. assignability) holds.
|
||||||
pub(crate) trait Constraints<'db>: Clone + Sized {
|
pub(crate) trait Constraints<'db>: Clone + Sized + std::fmt::Debug {
|
||||||
/// Returns a constraint set that never holds
|
/// Returns a constraint set that never holds
|
||||||
fn unsatisfiable(db: &'db dyn Db) -> Self;
|
fn unsatisfiable(db: &'db dyn Db) -> Self;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -78,9 +78,9 @@ use crate::types::signatures::{CallableSignature, Signature};
|
||||||
use crate::types::visitor::any_over_type;
|
use crate::types::visitor::any_over_type;
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
BoundMethodType, BoundTypeVarInstance, CallableType, ClassBase, ClassLiteral, ClassType,
|
BoundMethodType, BoundTypeVarInstance, CallableType, ClassBase, ClassLiteral, ClassType,
|
||||||
DeprecatedInstance, DynamicType, FindLegacyTypeVarsVisitor, HasRelationToVisitor,
|
DeprecatedInstance, DynamicType, FindLegacyTypeVarsVisitor, HasRelationToVisitor, KnownClass,
|
||||||
IsEquivalentVisitor, KnownClass, NormalizedVisitor, SpecialFormType, Truthiness, Type,
|
NormalizedVisitor, SpecialFormType, Truthiness, Type, TypeMapping, TypeRelation, UnionBuilder,
|
||||||
TypeMapping, TypeRelation, UnionBuilder, all_members, binding_type, walk_type_mapping,
|
all_members, binding_type, walk_type_mapping,
|
||||||
};
|
};
|
||||||
use crate::{Db, FxOrderSet, ModuleName, resolve_module};
|
use crate::{Db, FxOrderSet, ModuleName, resolve_module};
|
||||||
|
|
||||||
|
|
@ -981,23 +981,6 @@ impl<'db> FunctionType<'db> {
|
||||||
&& self.signature(db).is_assignable_to(db, other.signature(db))
|
&& self.signature(db).is_assignable_to(db, other.signature(db))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn is_equivalent_to_impl<C: Constraints<'db>>(
|
|
||||||
self,
|
|
||||||
db: &'db dyn Db,
|
|
||||||
other: Self,
|
|
||||||
visitor: &IsEquivalentVisitor<'db, C>,
|
|
||||||
) -> C {
|
|
||||||
if self.normalized(db) == other.normalized(db) {
|
|
||||||
return C::always_satisfiable(db);
|
|
||||||
}
|
|
||||||
if self.literal(db) != other.literal(db) {
|
|
||||||
return C::unsatisfiable(db);
|
|
||||||
}
|
|
||||||
let self_signature = self.signature(db);
|
|
||||||
let other_signature = other.signature(db);
|
|
||||||
self_signature.is_equivalent_to_impl(db, other_signature, visitor)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn find_legacy_typevars_impl(
|
pub(crate) fn find_legacy_typevars_impl(
|
||||||
self,
|
self,
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
|
|
|
||||||
|
|
@ -16,9 +16,9 @@ use crate::types::signatures::{Parameter, Parameters, Signature};
|
||||||
use crate::types::tuple::{TupleSpec, TupleType, walk_tuple_type};
|
use crate::types::tuple::{TupleSpec, TupleType, walk_tuple_type};
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
ApplyTypeMappingVisitor, BoundTypeVarInstance, FindLegacyTypeVarsVisitor, HasRelationToVisitor,
|
ApplyTypeMappingVisitor, BoundTypeVarInstance, FindLegacyTypeVarsVisitor, HasRelationToVisitor,
|
||||||
IsEquivalentVisitor, KnownClass, KnownInstanceType, MaterializationKind, NormalizedVisitor,
|
KnownClass, KnownInstanceType, MaterializationKind, NormalizedVisitor, Type, TypeMapping,
|
||||||
Type, TypeMapping, TypeRelation, TypeVarBoundOrConstraints, TypeVarInstance, TypeVarVariance,
|
TypeRelation, TypeVarBoundOrConstraints, TypeVarInstance, TypeVarVariance, UnionType,
|
||||||
UnionType, binding_type, declaration_type,
|
binding_type, declaration_type,
|
||||||
};
|
};
|
||||||
use crate::{Db, FxOrderSet};
|
use crate::{Db, FxOrderSet};
|
||||||
|
|
||||||
|
|
@ -805,7 +805,9 @@ impl<'db> Specialization<'db> {
|
||||||
{
|
{
|
||||||
match relation {
|
match relation {
|
||||||
TypeRelation::Assignability => continue,
|
TypeRelation::Assignability => continue,
|
||||||
TypeRelation::Subtyping => return C::unsatisfiable(db),
|
TypeRelation::Subtyping => {
|
||||||
|
return C::from_bool(db, self_type.is_dynamic() && other_type.is_dynamic());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -841,58 +843,6 @@ impl<'db> Specialization<'db> {
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn is_equivalent_to_impl<C: Constraints<'db>>(
|
|
||||||
self,
|
|
||||||
db: &'db dyn Db,
|
|
||||||
other: Specialization<'db>,
|
|
||||||
visitor: &IsEquivalentVisitor<'db, C>,
|
|
||||||
) -> C {
|
|
||||||
if self.materialization_kind(db) != other.materialization_kind(db) {
|
|
||||||
return C::unsatisfiable(db);
|
|
||||||
}
|
|
||||||
let generic_context = self.generic_context(db);
|
|
||||||
if generic_context != other.generic_context(db) {
|
|
||||||
return C::unsatisfiable(db);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut result = C::always_satisfiable(db);
|
|
||||||
for ((bound_typevar, self_type), other_type) in (generic_context.variables(db).into_iter())
|
|
||||||
.zip(self.types(db))
|
|
||||||
.zip(other.types(db))
|
|
||||||
{
|
|
||||||
// Equivalence of each type in the specialization depends on the variance of the
|
|
||||||
// corresponding typevar:
|
|
||||||
// - covariant: verify that self_type == other_type
|
|
||||||
// - contravariant: verify that other_type == self_type
|
|
||||||
// - invariant: verify that self_type == other_type
|
|
||||||
// - bivariant: skip, can't make equivalence false
|
|
||||||
let compatible = match bound_typevar.variance(db) {
|
|
||||||
TypeVarVariance::Invariant
|
|
||||||
| TypeVarVariance::Covariant
|
|
||||||
| TypeVarVariance::Contravariant => {
|
|
||||||
self_type.is_equivalent_to_impl(db, *other_type, visitor)
|
|
||||||
}
|
|
||||||
TypeVarVariance::Bivariant => C::always_satisfiable(db),
|
|
||||||
};
|
|
||||||
if result.intersect(db, compatible).is_never_satisfied(db) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match (self.tuple_inner(db), other.tuple_inner(db)) {
|
|
||||||
(Some(_), None) | (None, Some(_)) => return C::unsatisfiable(db),
|
|
||||||
(None, None) => {}
|
|
||||||
(Some(self_tuple), Some(other_tuple)) => {
|
|
||||||
let compatible = self_tuple.is_equivalent_to_impl(db, other_tuple, visitor);
|
|
||||||
if result.intersect(db, compatible).is_never_satisfied(db) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn find_legacy_typevars_impl(
|
pub(crate) fn find_legacy_typevars_impl(
|
||||||
self,
|
self,
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,7 @@ use crate::types::protocol_class::walk_protocol_interface;
|
||||||
use crate::types::tuple::{TupleSpec, TupleType};
|
use crate::types::tuple::{TupleSpec, TupleType};
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
ApplyTypeMappingVisitor, ClassBase, FindLegacyTypeVarsVisitor, HasRelationToVisitor,
|
ApplyTypeMappingVisitor, ClassBase, FindLegacyTypeVarsVisitor, HasRelationToVisitor,
|
||||||
IsDisjointVisitor, IsEquivalentVisitor, NormalizedVisitor, TypeMapping, TypeRelation,
|
IsDisjointVisitor, NormalizedVisitor, TypeMapping, TypeRelation, VarianceInferable,
|
||||||
VarianceInferable,
|
|
||||||
};
|
};
|
||||||
use crate::{Db, FxOrderSet};
|
use crate::{Db, FxOrderSet};
|
||||||
|
|
||||||
|
|
@ -278,24 +277,6 @@ impl<'db> NominalInstanceType<'db> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn is_equivalent_to_impl<C: Constraints<'db>>(
|
|
||||||
self,
|
|
||||||
db: &'db dyn Db,
|
|
||||||
other: Self,
|
|
||||||
visitor: &IsEquivalentVisitor<'db, C>,
|
|
||||||
) -> C {
|
|
||||||
match (self.0, other.0) {
|
|
||||||
(
|
|
||||||
NominalInstanceInner::ExactTuple(tuple1),
|
|
||||||
NominalInstanceInner::ExactTuple(tuple2),
|
|
||||||
) => tuple1.is_equivalent_to_impl(db, tuple2, visitor),
|
|
||||||
(NominalInstanceInner::NonTuple(class1), NominalInstanceInner::NonTuple(class2)) => {
|
|
||||||
class1.is_equivalent_to_impl(db, class2, visitor)
|
|
||||||
}
|
|
||||||
_ => C::unsatisfiable(db),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn is_disjoint_from_impl<C: Constraints<'db>>(
|
pub(super) fn is_disjoint_from_impl<C: Constraints<'db>>(
|
||||||
self,
|
self,
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
|
|
@ -482,13 +463,6 @@ impl<'db> ProtocolInstanceType<'db> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a "normalized" version of this `Protocol` type.
|
|
||||||
///
|
|
||||||
/// See [`Type::normalized`] for more details.
|
|
||||||
pub(super) fn normalized(self, db: &'db dyn Db) -> Type<'db> {
|
|
||||||
self.normalized_impl(db, &NormalizedVisitor::default())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return a "normalized" version of this `Protocol` type.
|
/// Return a "normalized" version of this `Protocol` type.
|
||||||
///
|
///
|
||||||
/// See [`Type::normalized`] for more details.
|
/// See [`Type::normalized`] for more details.
|
||||||
|
|
@ -524,32 +498,21 @@ impl<'db> ProtocolInstanceType<'db> {
|
||||||
self,
|
self,
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
other: Self,
|
other: Self,
|
||||||
_relation: TypeRelation,
|
relation: TypeRelation,
|
||||||
visitor: &HasRelationToVisitor<'db, C>,
|
visitor: &HasRelationToVisitor<'db, C>,
|
||||||
) -> C {
|
) -> C {
|
||||||
other
|
other
|
||||||
.inner
|
.inner
|
||||||
.interface(db)
|
.interface(db)
|
||||||
.is_sub_interface_of(db, self.inner.interface(db), visitor)
|
.is_sub_interface_of(db, self.inner.interface(db), visitor)
|
||||||
}
|
.or(db, || {
|
||||||
|
Type::object(db).has_relation_to_impl(
|
||||||
/// Return `true` if this protocol type is equivalent to the protocol `other`.
|
db,
|
||||||
///
|
Type::ProtocolInstance(other),
|
||||||
/// TODO: consider the types of the members as well as their existence
|
relation,
|
||||||
pub(super) fn is_equivalent_to_impl<C: Constraints<'db>>(
|
visitor,
|
||||||
self,
|
)
|
||||||
db: &'db dyn Db,
|
})
|
||||||
other: Self,
|
|
||||||
_visitor: &IsEquivalentVisitor<'db, C>,
|
|
||||||
) -> C {
|
|
||||||
if self == other {
|
|
||||||
return C::always_satisfiable(db);
|
|
||||||
}
|
|
||||||
let self_normalized = self.normalized(db);
|
|
||||||
if self_normalized == Type::ProtocolInstance(other) {
|
|
||||||
return C::always_satisfiable(db);
|
|
||||||
}
|
|
||||||
C::from_bool(db, self_normalized == other.normalized(db))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return `true` if this protocol type is disjoint from the protocol `other`.
|
/// Return `true` if this protocol type is disjoint from the protocol `other`.
|
||||||
|
|
|
||||||
|
|
@ -173,16 +173,16 @@ mod stable {
|
||||||
forall types s, t. s.is_assignable_to(db, union(db, [s, t])) && t.is_assignable_to(db, union(db, [s, t]))
|
forall types s, t. s.is_assignable_to(db, union(db, [s, t])) && t.is_assignable_to(db, union(db, [s, t]))
|
||||||
);
|
);
|
||||||
|
|
||||||
// Only `Never` is a subtype of `Any`.
|
// Only `Never`/`Any` are subtypes of `Any`.
|
||||||
type_property_test!(
|
type_property_test!(
|
||||||
only_never_is_subtype_of_any, db,
|
only_never_is_subtype_of_any, db,
|
||||||
forall types s. !s.is_equivalent_to(db, Type::Never) => !s.is_subtype_of(db, Type::any())
|
forall types s. !s.is_equivalent_to(db, Type::Never) => s.is_dynamic() || !s.is_subtype_of(db, Type::any())
|
||||||
);
|
);
|
||||||
|
|
||||||
// Only `object` is a supertype of `Any`.
|
// Only `object`/`Any` are supertypes of `Any`.
|
||||||
type_property_test!(
|
type_property_test!(
|
||||||
only_object_is_supertype_of_any, db,
|
only_object_is_supertype_of_any, db,
|
||||||
forall types t. !t.is_equivalent_to(db, Type::object(db)) => !Type::any().is_subtype_of(db, t)
|
forall types t. !t.is_equivalent_to(db, Type::object(db)) => t.is_dynamic() || !Type::any().is_subtype_of(db, t)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Equivalence is commutative.
|
// Equivalence is commutative.
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,8 @@ use crate::types::constraints::{ConstraintSet, Constraints, IteratorConstraintsE
|
||||||
use crate::types::generics::{GenericContext, walk_generic_context};
|
use crate::types::generics::{GenericContext, walk_generic_context};
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
ApplyTypeMappingVisitor, BindingContext, BoundTypeVarInstance, FindLegacyTypeVarsVisitor,
|
ApplyTypeMappingVisitor, BindingContext, BoundTypeVarInstance, FindLegacyTypeVarsVisitor,
|
||||||
HasRelationToVisitor, IsEquivalentVisitor, KnownClass, MaterializationKind, NormalizedVisitor,
|
HasRelationToVisitor, KnownClass, MaterializationKind, NormalizedVisitor, TypeMapping,
|
||||||
TypeMapping, TypeRelation, VarianceInferable, todo_type,
|
TypeRelation, VarianceInferable, todo_type,
|
||||||
};
|
};
|
||||||
use crate::{Db, FxOrderSet};
|
use crate::{Db, FxOrderSet};
|
||||||
use ruff_python_ast::{self as ast, name::Name};
|
use ruff_python_ast::{self as ast, name::Name};
|
||||||
|
|
@ -197,31 +197,6 @@ impl<'db> CallableSignature<'db> {
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check whether this callable type is equivalent to another callable type.
|
|
||||||
///
|
|
||||||
/// See [`Type::is_equivalent_to`] for more details.
|
|
||||||
pub(crate) fn is_equivalent_to_impl<C: Constraints<'db>>(
|
|
||||||
&self,
|
|
||||||
db: &'db dyn Db,
|
|
||||||
other: &Self,
|
|
||||||
visitor: &IsEquivalentVisitor<'db, C>,
|
|
||||||
) -> C {
|
|
||||||
match (self.overloads.as_slice(), other.overloads.as_slice()) {
|
|
||||||
([self_signature], [other_signature]) => {
|
|
||||||
// Common case: both callable types contain a single signature, use the custom
|
|
||||||
// equivalence check instead of delegating it to the subtype check.
|
|
||||||
self_signature.is_equivalent_to_impl(db, other_signature, visitor)
|
|
||||||
}
|
|
||||||
(_, _) => {
|
|
||||||
if self == other {
|
|
||||||
return C::always_satisfiable(db);
|
|
||||||
}
|
|
||||||
self.is_subtype_of_impl::<C>(db, other)
|
|
||||||
.and(db, || other.is_subtype_of_impl(db, self))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'db> IntoIterator for &'a CallableSignature<'db> {
|
impl<'a, 'db> IntoIterator for &'a CallableSignature<'db> {
|
||||||
|
|
@ -517,91 +492,6 @@ impl<'db> Signature<'db> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return `true` if `self` has exactly the same set of possible static materializations as
|
|
||||||
/// `other` (if `self` represents the same set of possible sets of possible runtime objects as
|
|
||||||
/// `other`).
|
|
||||||
pub(crate) fn is_equivalent_to_impl<C: Constraints<'db>>(
|
|
||||||
&self,
|
|
||||||
db: &'db dyn Db,
|
|
||||||
other: &Signature<'db>,
|
|
||||||
visitor: &IsEquivalentVisitor<'db, C>,
|
|
||||||
) -> C {
|
|
||||||
let mut result = C::always_satisfiable(db);
|
|
||||||
let mut check_types = |self_type: Option<Type<'db>>, other_type: Option<Type<'db>>| {
|
|
||||||
let self_type = self_type.unwrap_or(Type::unknown());
|
|
||||||
let other_type = other_type.unwrap_or(Type::unknown());
|
|
||||||
!result
|
|
||||||
.intersect(db, self_type.is_equivalent_to_impl(db, other_type, visitor))
|
|
||||||
.is_never_satisfied(db)
|
|
||||||
};
|
|
||||||
|
|
||||||
if self.parameters.is_gradual() != other.parameters.is_gradual() {
|
|
||||||
return C::unsatisfiable(db);
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.parameters.len() != other.parameters.len() {
|
|
||||||
return C::unsatisfiable(db);
|
|
||||||
}
|
|
||||||
|
|
||||||
if !check_types(self.return_ty, other.return_ty) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (self_parameter, other_parameter) in self.parameters.iter().zip(&other.parameters) {
|
|
||||||
match (self_parameter.kind(), other_parameter.kind()) {
|
|
||||||
(
|
|
||||||
ParameterKind::PositionalOnly {
|
|
||||||
default_type: self_default,
|
|
||||||
..
|
|
||||||
},
|
|
||||||
ParameterKind::PositionalOnly {
|
|
||||||
default_type: other_default,
|
|
||||||
..
|
|
||||||
},
|
|
||||||
) if self_default.is_some() == other_default.is_some() => {}
|
|
||||||
|
|
||||||
(
|
|
||||||
ParameterKind::PositionalOrKeyword {
|
|
||||||
name: self_name,
|
|
||||||
default_type: self_default,
|
|
||||||
},
|
|
||||||
ParameterKind::PositionalOrKeyword {
|
|
||||||
name: other_name,
|
|
||||||
default_type: other_default,
|
|
||||||
},
|
|
||||||
) if self_default.is_some() == other_default.is_some()
|
|
||||||
&& self_name == other_name => {}
|
|
||||||
|
|
||||||
(ParameterKind::Variadic { .. }, ParameterKind::Variadic { .. }) => {}
|
|
||||||
|
|
||||||
(
|
|
||||||
ParameterKind::KeywordOnly {
|
|
||||||
name: self_name,
|
|
||||||
default_type: self_default,
|
|
||||||
},
|
|
||||||
ParameterKind::KeywordOnly {
|
|
||||||
name: other_name,
|
|
||||||
default_type: other_default,
|
|
||||||
},
|
|
||||||
) if self_default.is_some() == other_default.is_some()
|
|
||||||
&& self_name == other_name => {}
|
|
||||||
|
|
||||||
(ParameterKind::KeywordVariadic { .. }, ParameterKind::KeywordVariadic { .. }) => {}
|
|
||||||
|
|
||||||
_ => return C::unsatisfiable(db),
|
|
||||||
}
|
|
||||||
|
|
||||||
if !check_types(
|
|
||||||
self_parameter.annotated_type(),
|
|
||||||
other_parameter.annotated_type(),
|
|
||||||
) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Implementation of subtyping and assignability for signature.
|
/// Implementation of subtyping and assignability for signature.
|
||||||
fn has_relation_to_impl<C: Constraints<'db>>(
|
fn has_relation_to_impl<C: Constraints<'db>>(
|
||||||
&self,
|
&self,
|
||||||
|
|
@ -701,8 +591,15 @@ impl<'db> Signature<'db> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// If either of the parameter lists is gradual (`...`), then it is assignable to and from
|
// If either of the parameter lists is gradual (`...`), then it is assignable to and from
|
||||||
// any other parameter list, but not a subtype or supertype of any other parameter list.
|
// any other parameter list, but only a subtype or supertype of another parameter list if
|
||||||
if self.parameters.is_gradual() || other.parameters.is_gradual() {
|
// that parameter list is also gradual.
|
||||||
|
if self.parameters.is_gradual() {
|
||||||
|
return if other.parameters.is_gradual() {
|
||||||
|
C::always_satisfiable(db)
|
||||||
|
} else {
|
||||||
|
C::from_bool(db, relation.is_assignability())
|
||||||
|
};
|
||||||
|
} else if other.parameters.is_gradual() {
|
||||||
return C::from_bool(db, relation.is_assignability());
|
return C::from_bool(db, relation.is_assignability());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,7 @@ use crate::types::class::{ClassType, KnownClass};
|
||||||
use crate::types::constraints::{Constraints, IteratorConstraintsExtension};
|
use crate::types::constraints::{Constraints, IteratorConstraintsExtension};
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
ApplyTypeMappingVisitor, BoundTypeVarInstance, FindLegacyTypeVarsVisitor, HasRelationToVisitor,
|
ApplyTypeMappingVisitor, BoundTypeVarInstance, FindLegacyTypeVarsVisitor, HasRelationToVisitor,
|
||||||
IsDisjointVisitor, IsEquivalentVisitor, NormalizedVisitor, Type, TypeMapping, TypeRelation,
|
IsDisjointVisitor, NormalizedVisitor, Type, TypeMapping, TypeRelation, UnionBuilder, UnionType,
|
||||||
UnionBuilder, UnionType,
|
|
||||||
};
|
};
|
||||||
use crate::util::subscript::{Nth, OutOfBoundsError, PyIndex, PySlice, StepSizeZeroError};
|
use crate::util::subscript::{Nth, OutOfBoundsError, PyIndex, PySlice, StepSizeZeroError};
|
||||||
use crate::{Db, FxOrderSet, Program};
|
use crate::{Db, FxOrderSet, Program};
|
||||||
|
|
@ -264,16 +263,6 @@ impl<'db> TupleType<'db> {
|
||||||
.has_relation_to_impl(db, other.tuple(db), relation, visitor)
|
.has_relation_to_impl(db, other.tuple(db), relation, visitor)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn is_equivalent_to_impl<C: Constraints<'db>>(
|
|
||||||
self,
|
|
||||||
db: &'db dyn Db,
|
|
||||||
other: Self,
|
|
||||||
visitor: &IsEquivalentVisitor<'db, C>,
|
|
||||||
) -> C {
|
|
||||||
self.tuple(db)
|
|
||||||
.is_equivalent_to_impl(db, other.tuple(db), visitor)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn is_single_valued(self, db: &'db dyn Db) -> bool {
|
pub(crate) fn is_single_valued(self, db: &'db dyn Db) -> bool {
|
||||||
self.tuple(db).is_single_valued(db)
|
self.tuple(db).is_single_valued(db)
|
||||||
}
|
}
|
||||||
|
|
@ -468,21 +457,6 @@ impl<'db> FixedLengthTuple<Type<'db>> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_equivalent_to_impl<C: Constraints<'db>>(
|
|
||||||
&self,
|
|
||||||
db: &'db dyn Db,
|
|
||||||
other: &Self,
|
|
||||||
visitor: &IsEquivalentVisitor<'db, C>,
|
|
||||||
) -> C {
|
|
||||||
C::from_bool(db, self.0.len() == other.0.len()).and(db, || {
|
|
||||||
(self.0.iter())
|
|
||||||
.zip(&other.0)
|
|
||||||
.when_all(db, |(self_ty, other_ty)| {
|
|
||||||
self_ty.is_equivalent_to_impl(db, *other_ty, visitor)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_single_valued(&self, db: &'db dyn Db) -> bool {
|
fn is_single_valued(&self, db: &'db dyn Db) -> bool {
|
||||||
self.0.iter().all(|ty| ty.is_single_valued(db))
|
self.0.iter().all(|ty| ty.is_single_valued(db))
|
||||||
}
|
}
|
||||||
|
|
@ -871,36 +845,6 @@ impl<'db> VariableLengthTuple<Type<'db>> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_equivalent_to_impl<C: Constraints<'db>>(
|
|
||||||
&self,
|
|
||||||
db: &'db dyn Db,
|
|
||||||
other: &Self,
|
|
||||||
visitor: &IsEquivalentVisitor<'db, C>,
|
|
||||||
) -> C {
|
|
||||||
self.variable
|
|
||||||
.is_equivalent_to_impl(db, other.variable, visitor)
|
|
||||||
.and(db, || {
|
|
||||||
(self.prenormalized_prefix_elements(db, None))
|
|
||||||
.zip_longest(other.prenormalized_prefix_elements(db, None))
|
|
||||||
.when_all(db, |pair| match pair {
|
|
||||||
EitherOrBoth::Both(self_ty, other_ty) => {
|
|
||||||
self_ty.is_equivalent_to_impl(db, other_ty, visitor)
|
|
||||||
}
|
|
||||||
EitherOrBoth::Left(_) | EitherOrBoth::Right(_) => C::unsatisfiable(db),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.and(db, || {
|
|
||||||
(self.prenormalized_suffix_elements(db, None))
|
|
||||||
.zip_longest(other.prenormalized_suffix_elements(db, None))
|
|
||||||
.when_all(db, |pair| match pair {
|
|
||||||
EitherOrBoth::Both(self_ty, other_ty) => {
|
|
||||||
self_ty.is_equivalent_to_impl(db, other_ty, visitor)
|
|
||||||
}
|
|
||||||
EitherOrBoth::Left(_) | EitherOrBoth::Right(_) => C::unsatisfiable(db),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'db> PyIndex<'db> for &VariableLengthTuple<Type<'db>> {
|
impl<'db> PyIndex<'db> for &VariableLengthTuple<Type<'db>> {
|
||||||
|
|
@ -1093,25 +1037,6 @@ impl<'db> Tuple<Type<'db>> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_equivalent_to_impl<C: Constraints<'db>>(
|
|
||||||
&self,
|
|
||||||
db: &'db dyn Db,
|
|
||||||
other: &Self,
|
|
||||||
visitor: &IsEquivalentVisitor<'db, C>,
|
|
||||||
) -> C {
|
|
||||||
match (self, other) {
|
|
||||||
(Tuple::Fixed(self_tuple), Tuple::Fixed(other_tuple)) => {
|
|
||||||
self_tuple.is_equivalent_to_impl(db, other_tuple, visitor)
|
|
||||||
}
|
|
||||||
(Tuple::Variable(self_tuple), Tuple::Variable(other_tuple)) => {
|
|
||||||
self_tuple.is_equivalent_to_impl(db, other_tuple, visitor)
|
|
||||||
}
|
|
||||||
(Tuple::Fixed(_), Tuple::Variable(_)) | (Tuple::Variable(_), Tuple::Fixed(_)) => {
|
|
||||||
C::unsatisfiable(db)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn is_disjoint_from_impl<C: Constraints<'db>>(
|
pub(super) fn is_disjoint_from_impl<C: Constraints<'db>>(
|
||||||
&self,
|
&self,
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue