Compare commits
13 Commits
main
...
dcreager/s
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b0e20ce8da | ||
|
|
cd7fae4968 | ||
|
|
7e5f1257d7 | ||
|
|
8c8173c626 | ||
|
|
ecbd3d7539 | ||
|
|
ede31a9dc3 | ||
|
|
69ae228db7 | ||
|
|
d8a8d343c9 | ||
|
|
83e749bfe2 | ||
|
|
02fed53477 | ||
|
|
d3853442da | ||
|
|
b6e790a84b | ||
|
|
a960a4a5df |
@@ -485,6 +485,7 @@ macro_rules! todo_type {
|
||||
pub use crate::types::definition::TypeDefinition;
|
||||
use crate::types::relation::{
|
||||
HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor, TypeRelation,
|
||||
default_equivalent_visitor,
|
||||
};
|
||||
pub(crate) use todo_type;
|
||||
|
||||
@@ -587,7 +588,7 @@ impl<'db> PropertyInstanceType<'db> {
|
||||
other: Self,
|
||||
inferable: InferableTypeVars<'_, 'db>,
|
||||
) -> ConstraintSet<'db> {
|
||||
self.is_equivalent_to_impl(db, other, inferable, &IsEquivalentVisitor::default())
|
||||
self.is_equivalent_to_impl(db, other, inferable, &default_equivalent_visitor(db))
|
||||
}
|
||||
|
||||
fn is_equivalent_to_impl(
|
||||
@@ -599,27 +600,27 @@ impl<'db> PropertyInstanceType<'db> {
|
||||
) -> ConstraintSet<'db> {
|
||||
let getter_equivalence = if let Some(getter) = self.getter(db) {
|
||||
let Some(other_getter) = other.getter(db) else {
|
||||
return ConstraintSet::from(false);
|
||||
return ConstraintSet::from_bool(db, false);
|
||||
};
|
||||
getter.is_equivalent_to_impl(db, other_getter, inferable, visitor)
|
||||
} else {
|
||||
if other.getter(db).is_some() {
|
||||
return ConstraintSet::from(false);
|
||||
return ConstraintSet::from_bool(db, false);
|
||||
}
|
||||
ConstraintSet::from(true)
|
||||
ConstraintSet::from_bool(db, true)
|
||||
};
|
||||
|
||||
let setter_equivalence = || {
|
||||
if let Some(setter) = self.setter(db) {
|
||||
let Some(other_setter) = other.setter(db) else {
|
||||
return ConstraintSet::from(false);
|
||||
return ConstraintSet::from_bool(db, false);
|
||||
};
|
||||
setter.is_equivalent_to_impl(db, other_setter, inferable, visitor)
|
||||
} else {
|
||||
if other.setter(db).is_some() {
|
||||
return ConstraintSet::from(false);
|
||||
return ConstraintSet::from_bool(db, false);
|
||||
}
|
||||
ConstraintSet::from(true)
|
||||
ConstraintSet::from_bool(db, true)
|
||||
}
|
||||
};
|
||||
|
||||
@@ -10506,7 +10507,7 @@ impl<'db> CallableType<'db> {
|
||||
disjointness_visitor: &IsDisjointVisitor<'db>,
|
||||
) -> ConstraintSet<'db> {
|
||||
if other.is_function_like(db) && !self.is_function_like(db) {
|
||||
return ConstraintSet::from(false);
|
||||
return ConstraintSet::from_bool(db, false);
|
||||
}
|
||||
|
||||
self.signatures(db).has_relation_to_impl(
|
||||
@@ -10530,13 +10531,20 @@ impl<'db> CallableType<'db> {
|
||||
visitor: &IsEquivalentVisitor<'db>,
|
||||
) -> ConstraintSet<'db> {
|
||||
if self == other {
|
||||
return ConstraintSet::from(true);
|
||||
return ConstraintSet::from_bool(db, true);
|
||||
}
|
||||
|
||||
ConstraintSet::from(self.is_function_like(db) == other.is_function_like(db)).and(db, || {
|
||||
self.signatures(db)
|
||||
.is_equivalent_to_impl(db, other.signatures(db), inferable, visitor)
|
||||
})
|
||||
ConstraintSet::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),
|
||||
inferable,
|
||||
visitor,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10721,7 +10729,7 @@ impl<'db> KnownBoundMethodType<'db> {
|
||||
) => self_property.when_equivalent_to(db, other_property, inferable),
|
||||
|
||||
(KnownBoundMethodType::StrStartswith(_), KnownBoundMethodType::StrStartswith(_)) => {
|
||||
ConstraintSet::from(self == other)
|
||||
ConstraintSet::from_bool(db, self == other)
|
||||
}
|
||||
|
||||
(
|
||||
@@ -10751,7 +10759,7 @@ impl<'db> KnownBoundMethodType<'db> {
|
||||
| (
|
||||
KnownBoundMethodType::GenericContextSpecializeConstrained(_),
|
||||
KnownBoundMethodType::GenericContextSpecializeConstrained(_),
|
||||
) => ConstraintSet::from(true),
|
||||
) => ConstraintSet::from_bool(db, true),
|
||||
|
||||
(
|
||||
KnownBoundMethodType::FunctionTypeDunderGet(_)
|
||||
@@ -10778,7 +10786,7 @@ impl<'db> KnownBoundMethodType<'db> {
|
||||
| KnownBoundMethodType::ConstraintSetSatisfies(_)
|
||||
| KnownBoundMethodType::ConstraintSetSatisfiedByAllTypeVars(_)
|
||||
| KnownBoundMethodType::GenericContextSpecializeConstrained(_),
|
||||
) => ConstraintSet::from(false),
|
||||
) => ConstraintSet::from_bool(db, false),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10810,7 +10818,7 @@ impl<'db> KnownBoundMethodType<'db> {
|
||||
) => self_property.is_equivalent_to_impl(db, other_property, inferable, visitor),
|
||||
|
||||
(KnownBoundMethodType::StrStartswith(_), KnownBoundMethodType::StrStartswith(_)) => {
|
||||
ConstraintSet::from(self == other)
|
||||
ConstraintSet::from_bool(db, self == other)
|
||||
}
|
||||
|
||||
(
|
||||
@@ -10824,7 +10832,7 @@ impl<'db> KnownBoundMethodType<'db> {
|
||||
| (
|
||||
KnownBoundMethodType::ConstraintSetNever,
|
||||
KnownBoundMethodType::ConstraintSetNever,
|
||||
) => ConstraintSet::from(true),
|
||||
) => ConstraintSet::from_bool(db, true),
|
||||
|
||||
(
|
||||
KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(left_constraints),
|
||||
@@ -10844,7 +10852,7 @@ impl<'db> KnownBoundMethodType<'db> {
|
||||
(
|
||||
KnownBoundMethodType::GenericContextSpecializeConstrained(left_generic_context),
|
||||
KnownBoundMethodType::GenericContextSpecializeConstrained(right_generic_context),
|
||||
) => ConstraintSet::from(left_generic_context == right_generic_context),
|
||||
) => ConstraintSet::from_bool(db, left_generic_context == right_generic_context),
|
||||
|
||||
(
|
||||
KnownBoundMethodType::FunctionTypeDunderGet(_)
|
||||
@@ -10871,7 +10879,7 @@ impl<'db> KnownBoundMethodType<'db> {
|
||||
| KnownBoundMethodType::ConstraintSetSatisfies(_)
|
||||
| KnownBoundMethodType::ConstraintSetSatisfiedByAllTypeVars(_)
|
||||
| KnownBoundMethodType::GenericContextSpecializeConstrained(_),
|
||||
) => ConstraintSet::from(false),
|
||||
) => ConstraintSet::from_bool(db, false),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12071,23 +12079,23 @@ impl<'db> UnionType<'db> {
|
||||
_visitor: &IsEquivalentVisitor<'db>,
|
||||
) -> ConstraintSet<'db> {
|
||||
if self == other {
|
||||
return ConstraintSet::from(true);
|
||||
return ConstraintSet::from_bool(db, true);
|
||||
}
|
||||
|
||||
let self_elements = self.elements(db);
|
||||
let other_elements = other.elements(db);
|
||||
|
||||
if self_elements.len() != other_elements.len() {
|
||||
return ConstraintSet::from(false);
|
||||
return ConstraintSet::from_bool(db, false);
|
||||
}
|
||||
|
||||
let sorted_self = self.normalized(db);
|
||||
|
||||
if sorted_self == Type::Union(other) {
|
||||
return ConstraintSet::from(true);
|
||||
return ConstraintSet::from_bool(db, true);
|
||||
}
|
||||
|
||||
ConstraintSet::from(sorted_self == other.normalized(db))
|
||||
ConstraintSet::from_bool(db, sorted_self == other.normalized(db))
|
||||
}
|
||||
|
||||
/// Identify some specific unions of known classes, currently the ones that `float` and
|
||||
@@ -12481,30 +12489,30 @@ impl<'db> IntersectionType<'db> {
|
||||
_visitor: &IsEquivalentVisitor<'db>,
|
||||
) -> ConstraintSet<'db> {
|
||||
if self == other {
|
||||
return ConstraintSet::from(true);
|
||||
return ConstraintSet::from_bool(db, true);
|
||||
}
|
||||
|
||||
let self_positive = self.positive(db);
|
||||
let other_positive = other.positive(db);
|
||||
|
||||
if self_positive.len() != other_positive.len() {
|
||||
return ConstraintSet::from(false);
|
||||
return ConstraintSet::from_bool(db, false);
|
||||
}
|
||||
|
||||
let self_negative = self.negative(db);
|
||||
let other_negative = other.negative(db);
|
||||
|
||||
if self_negative.len() != other_negative.len() {
|
||||
return ConstraintSet::from(false);
|
||||
return ConstraintSet::from_bool(db, false);
|
||||
}
|
||||
|
||||
let sorted_self = self.normalized(db);
|
||||
|
||||
if sorted_self == other {
|
||||
return ConstraintSet::from(true);
|
||||
return ConstraintSet::from_bool(db, true);
|
||||
}
|
||||
|
||||
ConstraintSet::from(sorted_self == other.normalized(db))
|
||||
ConstraintSet::from_bool(db, sorted_self == other.normalized(db))
|
||||
}
|
||||
|
||||
/// Returns an iterator over the positive elements of the intersection. If
|
||||
|
||||
@@ -1313,7 +1313,7 @@ impl<'db> Bindings<'db> {
|
||||
if !overload.parameter_types().is_empty() {
|
||||
return;
|
||||
}
|
||||
let constraints = ConstraintSet::from(true);
|
||||
let constraints = ConstraintSet::from_bool(db, true);
|
||||
let tracked = TrackedConstraintSet::new(db, constraints);
|
||||
overload.set_return_type(Type::KnownInstance(
|
||||
KnownInstanceType::ConstraintSet(tracked),
|
||||
@@ -1324,7 +1324,7 @@ impl<'db> Bindings<'db> {
|
||||
if !overload.parameter_types().is_empty() {
|
||||
return;
|
||||
}
|
||||
let constraints = ConstraintSet::from(false);
|
||||
let constraints = ConstraintSet::from_bool(db, false);
|
||||
let tracked = TrackedConstraintSet::new(db, constraints);
|
||||
overload.set_return_type(Type::KnownInstance(
|
||||
KnownInstanceType::ConstraintSet(tracked),
|
||||
|
||||
@@ -37,6 +37,7 @@ use crate::types::member::{Member, class_member};
|
||||
use crate::types::mro::{DynamicMroError, DynamicMroErrorKind};
|
||||
use crate::types::relation::{
|
||||
HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor, TypeRelation,
|
||||
default_disjoint_visitor, default_relation_visitor,
|
||||
};
|
||||
use crate::types::signatures::{CallableSignature, Parameter, Parameters, Signature};
|
||||
use crate::types::tuple::{TupleSpec, TupleType};
|
||||
@@ -1063,8 +1064,8 @@ impl<'db> ClassType<'db> {
|
||||
other,
|
||||
inferable,
|
||||
TypeRelation::Subtyping,
|
||||
&HasRelationToVisitor::default(),
|
||||
&IsDisjointVisitor::default(),
|
||||
&default_relation_visitor(db),
|
||||
&default_disjoint_visitor(db),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1083,42 +1084,45 @@ impl<'db> ClassType<'db> {
|
||||
TypeRelation::Subtyping
|
||||
| TypeRelation::Redundancy
|
||||
| TypeRelation::SubtypingAssuming(_) => {
|
||||
ConstraintSet::from(other.is_object(db))
|
||||
ConstraintSet::from_bool(db, other.is_object(db))
|
||||
}
|
||||
TypeRelation::Assignability | TypeRelation::ConstraintSetAssignability => {
|
||||
ConstraintSet::from(!other.is_final(db))
|
||||
ConstraintSet::from_bool(db, !other.is_final(db))
|
||||
}
|
||||
},
|
||||
|
||||
// Protocol, Generic, and TypedDict are special bases that don't match ClassType.
|
||||
ClassBase::Protocol | ClassBase::Generic | ClassBase::TypedDict => {
|
||||
ConstraintSet::from(false)
|
||||
ConstraintSet::from_bool(db, false)
|
||||
}
|
||||
|
||||
ClassBase::Class(base) => match (base, other) {
|
||||
// Two non-generic classes match if they have the same class literal.
|
||||
(ClassType::NonGeneric(base_literal), ClassType::NonGeneric(other_literal)) => {
|
||||
ConstraintSet::from(base_literal == other_literal)
|
||||
ConstraintSet::from_bool(db, base_literal == other_literal)
|
||||
}
|
||||
|
||||
// Two generic classes match if they have the same origin and compatible specializations.
|
||||
(ClassType::Generic(base), ClassType::Generic(other)) => {
|
||||
ConstraintSet::from(base.origin(db) == other.origin(db)).and(db, || {
|
||||
base.specialization(db).has_relation_to_impl(
|
||||
db,
|
||||
other.specialization(db),
|
||||
inferable,
|
||||
relation,
|
||||
relation_visitor,
|
||||
disjointness_visitor,
|
||||
)
|
||||
})
|
||||
ConstraintSet::from_bool(db, base.origin(db) == other.origin(db)).and(
|
||||
db,
|
||||
|| {
|
||||
base.specialization(db).has_relation_to_impl(
|
||||
db,
|
||||
other.specialization(db),
|
||||
inferable,
|
||||
relation,
|
||||
relation_visitor,
|
||||
disjointness_visitor,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// Generic and non-generic classes don't match.
|
||||
(ClassType::Generic(_), ClassType::NonGeneric(_))
|
||||
| (ClassType::NonGeneric(_), ClassType::Generic(_)) => {
|
||||
ConstraintSet::from(false)
|
||||
ConstraintSet::from_bool(db, false)
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -1133,18 +1137,18 @@ impl<'db> ClassType<'db> {
|
||||
visitor: &IsEquivalentVisitor<'db>,
|
||||
) -> ConstraintSet<'db> {
|
||||
if self == other {
|
||||
return ConstraintSet::from(true);
|
||||
return ConstraintSet::from_bool(db, true);
|
||||
}
|
||||
|
||||
match (self, other) {
|
||||
// Two non-generic classes are only equivalent if they are equal (handled above).
|
||||
// A non-generic class is never equivalent to a generic class.
|
||||
(ClassType::NonGeneric(_), _) | (_, ClassType::NonGeneric(_)) => {
|
||||
ConstraintSet::from(false)
|
||||
ConstraintSet::from_bool(db, false)
|
||||
}
|
||||
|
||||
(ClassType::Generic(this), ClassType::Generic(other)) => {
|
||||
ConstraintSet::from(this.origin(db) == other.origin(db)).and(db, || {
|
||||
ConstraintSet::from_bool(db, this.origin(db) == other.origin(db)).and(db, || {
|
||||
this.specialization(db).is_equivalent_to_impl(
|
||||
db,
|
||||
other.specialization(db),
|
||||
@@ -6523,7 +6527,7 @@ impl KnownClass {
|
||||
db: &'db dyn Db,
|
||||
other: ClassType<'db>,
|
||||
) -> ConstraintSet<'db> {
|
||||
ConstraintSet::from(self.is_subclass_of(db, other))
|
||||
ConstraintSet::from_bool(db, self.is_subclass_of(db, other))
|
||||
}
|
||||
|
||||
/// Return the module in which we should look up the definition for this class
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1123,7 +1123,7 @@ impl<'db> FunctionType<'db> {
|
||||
disjointness_visitor: &IsDisjointVisitor<'db>,
|
||||
) -> ConstraintSet<'db> {
|
||||
if self.literal(db) != other.literal(db) {
|
||||
return ConstraintSet::from(false);
|
||||
return ConstraintSet::from_bool(db, false);
|
||||
}
|
||||
|
||||
let self_signature = self.signature(db);
|
||||
@@ -1146,10 +1146,10 @@ impl<'db> FunctionType<'db> {
|
||||
visitor: &IsEquivalentVisitor<'db>,
|
||||
) -> ConstraintSet<'db> {
|
||||
if self.normalized(db) == other.normalized(db) {
|
||||
return ConstraintSet::from(true);
|
||||
return ConstraintSet::from_bool(db, true);
|
||||
}
|
||||
if self.literal(db) != other.literal(db) {
|
||||
return ConstraintSet::from(false);
|
||||
return ConstraintSet::from_bool(db, false);
|
||||
}
|
||||
let self_signature = self.signature(db);
|
||||
let other_signature = other.signature(db);
|
||||
|
||||
@@ -17,6 +17,7 @@ use crate::types::constraints::{ConstraintSet, IteratorConstraintsExtension};
|
||||
use crate::types::instance::{Protocol, ProtocolInstanceType};
|
||||
use crate::types::relation::{
|
||||
HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor, TypeRelation,
|
||||
default_disjoint_visitor, default_relation_visitor,
|
||||
};
|
||||
use crate::types::signatures::Parameters;
|
||||
use crate::types::tuple::{TupleSpec, TupleType, walk_tuple_type};
|
||||
@@ -793,12 +794,12 @@ fn is_subtype_in_invariant_position<'db>(
|
||||
// This should be removed and properly handled in the respective
|
||||
// `(Type::TypeVar(_), _) | (_, Type::TypeVar(_))` branch of
|
||||
// `Type::has_relation_to_impl`. Right now, we cannot generally
|
||||
// return `ConstraintSet::from(true)` from that branch, as that
|
||||
// return `ConstraintSet::from_bool(db, true)` from that branch, as that
|
||||
// leads to union simplification, which means that we lose track
|
||||
// of type variables without recording the constraints under which
|
||||
// the relation holds.
|
||||
if matches!(base, Type::TypeVar(_)) || matches!(derived, Type::TypeVar(_)) {
|
||||
return ConstraintSet::from(true);
|
||||
return ConstraintSet::from_bool(db, true);
|
||||
}
|
||||
|
||||
derived.has_relation_to_impl(
|
||||
@@ -1272,7 +1273,7 @@ impl<'db> Specialization<'db> {
|
||||
) -> ConstraintSet<'db> {
|
||||
let generic_context = self.generic_context(db);
|
||||
if generic_context != other.generic_context(db) {
|
||||
return ConstraintSet::from(false);
|
||||
return ConstraintSet::from_bool(db, false);
|
||||
}
|
||||
|
||||
if let (Some(self_tuple), Some(other_tuple)) = (self.tuple_inner(db), other.tuple_inner(db))
|
||||
@@ -1331,7 +1332,7 @@ impl<'db> Specialization<'db> {
|
||||
relation_visitor,
|
||||
disjointness_visitor,
|
||||
),
|
||||
TypeVarVariance::Bivariant => ConstraintSet::from(true),
|
||||
TypeVarVariance::Bivariant => ConstraintSet::from_bool(db, true),
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1346,8 +1347,8 @@ impl<'db> Specialization<'db> {
|
||||
db,
|
||||
other,
|
||||
inferable,
|
||||
&IsDisjointVisitor::default(),
|
||||
&HasRelationToVisitor::default(),
|
||||
&default_disjoint_visitor(db),
|
||||
&default_relation_visitor(db),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1361,7 +1362,7 @@ impl<'db> Specialization<'db> {
|
||||
) -> ConstraintSet<'db> {
|
||||
let generic_context = self.generic_context(db);
|
||||
if generic_context != other.generic_context(db) {
|
||||
return ConstraintSet::from(true);
|
||||
return ConstraintSet::from_bool(db, true);
|
||||
}
|
||||
|
||||
if let (Some(self_tuple), Some(other_tuple)) = (self.tuple_inner(db), other.tuple_inner(db))
|
||||
@@ -1399,13 +1400,13 @@ impl<'db> Specialization<'db> {
|
||||
),
|
||||
|
||||
// If `Foo[T]` is covariant in `T`, `Foo[Never]` is a subtype of `Foo[A]` and `Foo[B]`
|
||||
TypeVarVariance::Covariant => ConstraintSet::from(false),
|
||||
TypeVarVariance::Covariant => ConstraintSet::from_bool(db, false),
|
||||
|
||||
// If `Foo[T]` is contravariant in `T`, `Foo[A | B]` is a subtype of `Foo[A]` and `Foo[B]`
|
||||
TypeVarVariance::Contravariant => ConstraintSet::from(false),
|
||||
TypeVarVariance::Contravariant => ConstraintSet::from_bool(db, false),
|
||||
|
||||
// If `Foo[T]` is bivariant in `T`, `Foo[A]` and `Foo[B]` are mutual subtypes.
|
||||
TypeVarVariance::Bivariant => ConstraintSet::from(false),
|
||||
TypeVarVariance::Bivariant => ConstraintSet::from_bool(db, false),
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -1418,14 +1419,14 @@ impl<'db> Specialization<'db> {
|
||||
visitor: &IsEquivalentVisitor<'db>,
|
||||
) -> ConstraintSet<'db> {
|
||||
if self.materialization_kind(db) != other.materialization_kind(db) {
|
||||
return ConstraintSet::from(false);
|
||||
return ConstraintSet::from_bool(db, false);
|
||||
}
|
||||
let generic_context = self.generic_context(db);
|
||||
if generic_context != other.generic_context(db) {
|
||||
return ConstraintSet::from(false);
|
||||
return ConstraintSet::from_bool(db, false);
|
||||
}
|
||||
|
||||
let mut result = ConstraintSet::from(true);
|
||||
let mut result = ConstraintSet::from_bool(db, true);
|
||||
for ((bound_typevar, self_type), other_type) in generic_context
|
||||
.variables(db)
|
||||
.zip(self.types(db))
|
||||
@@ -1443,7 +1444,7 @@ impl<'db> Specialization<'db> {
|
||||
| TypeVarVariance::Contravariant => {
|
||||
self_type.is_equivalent_to_impl(db, *other_type, inferable, visitor)
|
||||
}
|
||||
TypeVarVariance::Bivariant => ConstraintSet::from(true),
|
||||
TypeVarVariance::Bivariant => ConstraintSet::from_bool(db, true),
|
||||
};
|
||||
if result.intersect(db, compatible).is_never_satisfied(db) {
|
||||
return result;
|
||||
@@ -1451,7 +1452,7 @@ impl<'db> Specialization<'db> {
|
||||
}
|
||||
|
||||
match (self.tuple_inner(db), other.tuple_inner(db)) {
|
||||
(Some(_), None) | (None, Some(_)) => return ConstraintSet::from(false),
|
||||
(Some(_), None) | (None, Some(_)) => return ConstraintSet::from_bool(db, false),
|
||||
(None, None) => {}
|
||||
(Some(self_tuple), Some(other_tuple)) => {
|
||||
let compatible =
|
||||
|
||||
@@ -13,6 +13,7 @@ use crate::types::generics::{InferableTypeVars, walk_specialization};
|
||||
use crate::types::protocol_class::{ProtocolClass, walk_protocol_interface};
|
||||
use crate::types::relation::{
|
||||
HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor, TypeRelation,
|
||||
default_disjoint_visitor, default_relation_visitor,
|
||||
};
|
||||
use crate::types::tuple::{TupleSpec, TupleType, walk_tuple_type};
|
||||
use crate::types::{
|
||||
@@ -179,7 +180,7 @@ impl<'db> Type<'db> {
|
||||
// recognise `str` as a subtype of `Container[str]`.
|
||||
structurally_satisfied.or(db, || {
|
||||
let Some(nominal_instance) = protocol.to_nominal_instance() else {
|
||||
return ConstraintSet::from(false);
|
||||
return ConstraintSet::from_bool(db, false);
|
||||
};
|
||||
|
||||
// if `self` and `other` are *both* protocols, we also need to treat `self` as if it
|
||||
@@ -411,7 +412,7 @@ impl<'db> NominalInstanceType<'db> {
|
||||
disjointness_visitor: &IsDisjointVisitor<'db>,
|
||||
) -> ConstraintSet<'db> {
|
||||
match (self.0, other.0) {
|
||||
(_, NominalInstanceInner::Object) => ConstraintSet::from(true),
|
||||
(_, NominalInstanceInner::Object) => ConstraintSet::from_bool(db, true),
|
||||
(
|
||||
NominalInstanceInner::ExactTuple(tuple1),
|
||||
NominalInstanceInner::ExactTuple(tuple2),
|
||||
@@ -447,12 +448,12 @@ impl<'db> NominalInstanceType<'db> {
|
||||
NominalInstanceInner::ExactTuple(tuple2),
|
||||
) => tuple1.is_equivalent_to_impl(db, tuple2, inferable, visitor),
|
||||
(NominalInstanceInner::Object, NominalInstanceInner::Object) => {
|
||||
ConstraintSet::from(true)
|
||||
ConstraintSet::from_bool(db, true)
|
||||
}
|
||||
(NominalInstanceInner::NonTuple(class1), NominalInstanceInner::NonTuple(class2)) => {
|
||||
class1.is_equivalent_to_impl(db, class2, inferable, visitor)
|
||||
}
|
||||
_ => ConstraintSet::from(false),
|
||||
_ => ConstraintSet::from_bool(db, false),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -465,9 +466,9 @@ impl<'db> NominalInstanceType<'db> {
|
||||
relation_visitor: &HasRelationToVisitor<'db>,
|
||||
) -> ConstraintSet<'db> {
|
||||
if self.is_object() || other.is_object() {
|
||||
return ConstraintSet::from(false);
|
||||
return ConstraintSet::from_bool(db, false);
|
||||
}
|
||||
let mut result = ConstraintSet::from(false);
|
||||
let mut result = ConstraintSet::from_bool(db, false);
|
||||
if let Some(self_spec) = self.tuple_spec(db) {
|
||||
if let Some(other_spec) = other.tuple_spec(db) {
|
||||
let compatible = self_spec.is_disjoint_from_impl(
|
||||
@@ -484,7 +485,8 @@ impl<'db> NominalInstanceType<'db> {
|
||||
}
|
||||
|
||||
result.or(db, || {
|
||||
ConstraintSet::from(
|
||||
ConstraintSet::from_bool(
|
||||
db,
|
||||
!self
|
||||
.class(db)
|
||||
.could_coexist_in_mro_with(db, other.class(db)),
|
||||
@@ -713,8 +715,8 @@ impl<'db> ProtocolInstanceType<'db> {
|
||||
protocol,
|
||||
InferableTypeVars::None,
|
||||
TypeRelation::Subtyping,
|
||||
&HasRelationToVisitor::default(),
|
||||
&IsDisjointVisitor::default(),
|
||||
&default_relation_visitor(db),
|
||||
&default_disjoint_visitor(db),
|
||||
)
|
||||
.is_always_satisfied(db)
|
||||
}
|
||||
@@ -780,13 +782,13 @@ impl<'db> ProtocolInstanceType<'db> {
|
||||
_visitor: &IsEquivalentVisitor<'db>,
|
||||
) -> ConstraintSet<'db> {
|
||||
if self == other {
|
||||
return ConstraintSet::from(true);
|
||||
return ConstraintSet::from_bool(db, true);
|
||||
}
|
||||
let self_normalized = self.normalized(db);
|
||||
if self_normalized == Type::ProtocolInstance(other) {
|
||||
return ConstraintSet::from(true);
|
||||
return ConstraintSet::from_bool(db, true);
|
||||
}
|
||||
ConstraintSet::from(self_normalized == other.normalized(db))
|
||||
ConstraintSet::from_bool(db, self_normalized == other.normalized(db))
|
||||
}
|
||||
|
||||
/// Return `true` if this protocol type is disjoint from the protocol `other`.
|
||||
@@ -796,12 +798,12 @@ impl<'db> ProtocolInstanceType<'db> {
|
||||
#[expect(clippy::unused_self)]
|
||||
pub(super) fn is_disjoint_from_impl(
|
||||
self,
|
||||
_db: &'db dyn Db,
|
||||
db: &'db dyn Db,
|
||||
_other: Self,
|
||||
_inferable: InferableTypeVars<'_, 'db>,
|
||||
_visitor: &IsDisjointVisitor<'db>,
|
||||
) -> ConstraintSet<'db> {
|
||||
ConstraintSet::from(false)
|
||||
ConstraintSet::from_bool(db, false)
|
||||
}
|
||||
|
||||
pub(crate) fn instance_member(self, db: &'db dyn Db, name: &str) -> PlaceAndQualifiers<'db> {
|
||||
|
||||
@@ -125,16 +125,16 @@ impl<'db> NewType<'db> {
|
||||
// base class, we don't have to keep looking.
|
||||
pub(crate) fn has_relation_to_impl(self, db: &'db dyn Db, other: Self) -> ConstraintSet<'db> {
|
||||
if self.is_equivalent_to_impl(db, other) {
|
||||
return ConstraintSet::from(true);
|
||||
return ConstraintSet::from_bool(db, true);
|
||||
}
|
||||
for base in self.iter_bases(db) {
|
||||
if let NewTypeBase::NewType(base_newtype) = base {
|
||||
if base_newtype.is_equivalent_to_impl(db, other) {
|
||||
return ConstraintSet::from(true);
|
||||
return ConstraintSet::from_bool(db, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
ConstraintSet::from(false)
|
||||
ConstraintSet::from_bool(db, false)
|
||||
}
|
||||
|
||||
pub(crate) fn is_disjoint_from_impl(self, db: &'db dyn Db, other: Self) -> ConstraintSet<'db> {
|
||||
|
||||
@@ -292,76 +292,44 @@ impl<'db> ProtocolInterface<'db> {
|
||||
) -> ConstraintSet<'db> {
|
||||
other.members(db).when_all(db, |other_member| {
|
||||
self.member_by_name(db, other_member.name)
|
||||
.when_some_and(|our_member| match (our_member.kind, other_member.kind) {
|
||||
// Method members are always immutable;
|
||||
// they can never be subtypes of/assignable to mutable attribute members.
|
||||
(ProtocolMemberKind::Method(_), ProtocolMemberKind::Other(_)) => {
|
||||
ConstraintSet::from(false)
|
||||
}
|
||||
.when_some_and(db, |our_member| {
|
||||
match (our_member.kind, other_member.kind) {
|
||||
// Method members are always immutable;
|
||||
// they can never be subtypes of/assignable to mutable attribute members.
|
||||
(ProtocolMemberKind::Method(_), ProtocolMemberKind::Other(_)) => {
|
||||
ConstraintSet::from_bool(db, false)
|
||||
}
|
||||
|
||||
// A property member can only be a subtype of an attribute member
|
||||
// if the property is readable *and* writable.
|
||||
//
|
||||
// TODO: this should also consider the types of the members on both sides.
|
||||
(ProtocolMemberKind::Property(property), ProtocolMemberKind::Other(_)) => {
|
||||
ConstraintSet::from(
|
||||
property.getter(db).is_some() && property.setter(db).is_some(),
|
||||
)
|
||||
}
|
||||
// A property member can only be a subtype of an attribute member
|
||||
// if the property is readable *and* writable.
|
||||
//
|
||||
// TODO: this should also consider the types of the members on both sides.
|
||||
(ProtocolMemberKind::Property(property), ProtocolMemberKind::Other(_)) => {
|
||||
ConstraintSet::from_bool(
|
||||
db,
|
||||
property.getter(db).is_some() && property.setter(db).is_some(),
|
||||
)
|
||||
}
|
||||
|
||||
// A `@property` member can never be a subtype of a method member, as it is not necessarily
|
||||
// accessible on the meta-type, whereas a method member must be.
|
||||
(ProtocolMemberKind::Property(_), ProtocolMemberKind::Method(_)) => {
|
||||
ConstraintSet::from(false)
|
||||
}
|
||||
// A `@property` member can never be a subtype of a method member, as it is not necessarily
|
||||
// accessible on the meta-type, whereas a method member must be.
|
||||
(ProtocolMemberKind::Property(_), ProtocolMemberKind::Method(_)) => {
|
||||
ConstraintSet::from_bool(db, false)
|
||||
}
|
||||
|
||||
// But an attribute member *can* be a subtype of a method member,
|
||||
// providing it is marked `ClassVar`
|
||||
(
|
||||
ProtocolMemberKind::Other(our_type),
|
||||
ProtocolMemberKind::Method(other_type),
|
||||
) => ConstraintSet::from(
|
||||
our_member.qualifiers.contains(TypeQualifiers::CLASS_VAR),
|
||||
)
|
||||
.and(db, || {
|
||||
our_type.has_relation_to_impl(
|
||||
// But an attribute member *can* be a subtype of a method member,
|
||||
// providing it is marked `ClassVar`
|
||||
(
|
||||
ProtocolMemberKind::Other(our_type),
|
||||
ProtocolMemberKind::Method(other_type),
|
||||
) => ConstraintSet::from_bool(
|
||||
db,
|
||||
Type::Callable(protocol_bind_self(db, other_type, None)),
|
||||
inferable,
|
||||
relation,
|
||||
relation_visitor,
|
||||
disjointness_visitor,
|
||||
)
|
||||
}),
|
||||
|
||||
(
|
||||
ProtocolMemberKind::Method(our_method),
|
||||
ProtocolMemberKind::Method(other_method),
|
||||
) => our_method.bind_self(db, None).has_relation_to_impl(
|
||||
db,
|
||||
protocol_bind_self(db, other_method, None),
|
||||
inferable,
|
||||
relation,
|
||||
relation_visitor,
|
||||
disjointness_visitor,
|
||||
),
|
||||
|
||||
(
|
||||
ProtocolMemberKind::Other(our_type),
|
||||
ProtocolMemberKind::Other(other_type),
|
||||
) => our_type
|
||||
.has_relation_to_impl(
|
||||
db,
|
||||
other_type,
|
||||
inferable,
|
||||
relation,
|
||||
relation_visitor,
|
||||
disjointness_visitor,
|
||||
our_member.qualifiers.contains(TypeQualifiers::CLASS_VAR),
|
||||
)
|
||||
.and(db, || {
|
||||
other_type.has_relation_to_impl(
|
||||
our_type.has_relation_to_impl(
|
||||
db,
|
||||
our_type,
|
||||
Type::Callable(protocol_bind_self(db, other_type, None)),
|
||||
inferable,
|
||||
relation,
|
||||
relation_visitor,
|
||||
@@ -369,14 +337,50 @@ impl<'db> ProtocolInterface<'db> {
|
||||
)
|
||||
}),
|
||||
|
||||
// TODO: finish assignability/subtyping between two `@property` members,
|
||||
// and between a `@property` member and a member of a different kind.
|
||||
(
|
||||
ProtocolMemberKind::Property(_)
|
||||
| ProtocolMemberKind::Method(_)
|
||||
| ProtocolMemberKind::Other(_),
|
||||
ProtocolMemberKind::Property(_),
|
||||
) => ConstraintSet::from(true),
|
||||
(
|
||||
ProtocolMemberKind::Method(our_method),
|
||||
ProtocolMemberKind::Method(other_method),
|
||||
) => our_method.bind_self(db, None).has_relation_to_impl(
|
||||
db,
|
||||
protocol_bind_self(db, other_method, None),
|
||||
inferable,
|
||||
relation,
|
||||
relation_visitor,
|
||||
disjointness_visitor,
|
||||
),
|
||||
|
||||
(
|
||||
ProtocolMemberKind::Other(our_type),
|
||||
ProtocolMemberKind::Other(other_type),
|
||||
) => our_type
|
||||
.has_relation_to_impl(
|
||||
db,
|
||||
other_type,
|
||||
inferable,
|
||||
relation,
|
||||
relation_visitor,
|
||||
disjointness_visitor,
|
||||
)
|
||||
.and(db, || {
|
||||
other_type.has_relation_to_impl(
|
||||
db,
|
||||
our_type,
|
||||
inferable,
|
||||
relation,
|
||||
relation_visitor,
|
||||
disjointness_visitor,
|
||||
)
|
||||
}),
|
||||
|
||||
// TODO: finish assignability/subtyping between two `@property` members,
|
||||
// and between a `@property` member and a member of a different kind.
|
||||
(
|
||||
ProtocolMemberKind::Property(_)
|
||||
| ProtocolMemberKind::Method(_)
|
||||
| ProtocolMemberKind::Other(_),
|
||||
ProtocolMemberKind::Property(_),
|
||||
) => ConstraintSet::from_bool(db, true),
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -713,7 +717,7 @@ impl<'a, 'db> ProtocolMember<'a, 'db> {
|
||||
match &self.kind {
|
||||
// TODO: implement disjointness for property/method members as well as attribute members
|
||||
ProtocolMemberKind::Property(_) | ProtocolMemberKind::Method(_) => {
|
||||
ConstraintSet::from(false)
|
||||
ConstraintSet::from_bool(db, false)
|
||||
}
|
||||
ProtocolMemberKind::Other(ty) => ty.is_disjoint_from_impl(
|
||||
db,
|
||||
@@ -764,7 +768,7 @@ impl<'a, 'db> ProtocolMember<'a, 'db> {
|
||||
)
|
||||
.place
|
||||
else {
|
||||
return ConstraintSet::from(false);
|
||||
return ConstraintSet::from_bool(db, false);
|
||||
};
|
||||
attribute_type
|
||||
};
|
||||
@@ -783,7 +787,7 @@ impl<'a, 'db> ProtocolMember<'a, 'db> {
|
||||
let fallback_other = other.literal_fallback_instance(db).unwrap_or(other);
|
||||
attribute_type
|
||||
.try_upcast_to_callable(db)
|
||||
.when_some_and(|callables| {
|
||||
.when_some_and(db, |callables| {
|
||||
callables
|
||||
.map(|callable| callable.apply_self(db, fallback_other))
|
||||
.has_relation_to_impl(
|
||||
@@ -797,13 +801,16 @@ impl<'a, 'db> ProtocolMember<'a, 'db> {
|
||||
})
|
||||
}
|
||||
// TODO: consider the types of the attribute on `other` for property members
|
||||
ProtocolMemberKind::Property(_) => ConstraintSet::from(matches!(
|
||||
other.member(db, self.name).place,
|
||||
Place::Defined(DefinedPlace {
|
||||
definedness: Definedness::AlwaysDefined,
|
||||
..
|
||||
})
|
||||
)),
|
||||
ProtocolMemberKind::Property(_) => ConstraintSet::from_bool(
|
||||
db,
|
||||
matches!(
|
||||
other.member(db, self.name).place,
|
||||
Place::Defined(DefinedPlace {
|
||||
definedness: Definedness::AlwaysDefined,
|
||||
..
|
||||
})
|
||||
),
|
||||
),
|
||||
ProtocolMemberKind::Other(member_type) => {
|
||||
let Place::Defined(DefinedPlace {
|
||||
ty: attribute_type,
|
||||
@@ -811,7 +818,7 @@ impl<'a, 'db> ProtocolMember<'a, 'db> {
|
||||
..
|
||||
}) = other.member(db, self.name).place
|
||||
else {
|
||||
return ConstraintSet::from(false);
|
||||
return ConstraintSet::from_bool(db, false);
|
||||
};
|
||||
member_type
|
||||
.has_relation_to_impl(
|
||||
|
||||
@@ -310,8 +310,8 @@ impl<'db> Type<'db> {
|
||||
target,
|
||||
inferable,
|
||||
relation,
|
||||
&HasRelationToVisitor::default(),
|
||||
&IsDisjointVisitor::default(),
|
||||
&default_relation_visitor(db),
|
||||
&default_disjoint_visitor(db),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -330,7 +330,7 @@ impl<'db> Type<'db> {
|
||||
// Note that we could do a full equivalence check here, but that would be both expensive
|
||||
// and unnecessary. This early return is only an optimisation.
|
||||
if (!relation.is_subtyping() || self.subtyping_is_always_reflexive()) && self == target {
|
||||
return ConstraintSet::from(true);
|
||||
return ConstraintSet::from_bool(db, true);
|
||||
}
|
||||
|
||||
// Handle constraint implication first. If either `self` or `target` is a typevar, check
|
||||
@@ -359,22 +359,22 @@ impl<'db> Type<'db> {
|
||||
match (self, target) {
|
||||
// Everything is a subtype of `object`.
|
||||
(_, Type::NominalInstance(instance)) if instance.is_object() => {
|
||||
ConstraintSet::from(true)
|
||||
ConstraintSet::from_bool(db, true)
|
||||
}
|
||||
(_, Type::ProtocolInstance(target)) if target.is_equivalent_to_object(db) => {
|
||||
ConstraintSet::from(true)
|
||||
ConstraintSet::from_bool(db, true)
|
||||
}
|
||||
|
||||
// `Never` is the bottom type, the empty set.
|
||||
// It is a subtype of all other types.
|
||||
(Type::Never, _) => ConstraintSet::from(true),
|
||||
(Type::Never, _) => ConstraintSet::from_bool(db, true),
|
||||
|
||||
// In some specific situations, `Any`/`Unknown`/`@Todo` can be simplified out of unions and intersections,
|
||||
// but this is not true for divergent types (and moving this case any lower down appears to cause
|
||||
// "too many cycle iterations" panics).
|
||||
(Type::Dynamic(DynamicType::Divergent(_)), _)
|
||||
| (_, Type::Dynamic(DynamicType::Divergent(_))) => {
|
||||
ConstraintSet::from(relation.is_assignability())
|
||||
ConstraintSet::from_bool(db, relation.is_assignability())
|
||||
}
|
||||
|
||||
(Type::TypeAlias(self_alias), _) => {
|
||||
@@ -409,7 +409,7 @@ impl<'db> Type<'db> {
|
||||
(Type::KnownInstance(KnownInstanceType::Field(field)), right)
|
||||
if relation.is_assignability() =>
|
||||
{
|
||||
field.default_type(db).when_none_or(|default_type| {
|
||||
field.default_type(db).when_none_or(db, |default_type| {
|
||||
default_type.has_relation_to_impl(
|
||||
db,
|
||||
right,
|
||||
@@ -434,31 +434,39 @@ impl<'db> Type<'db> {
|
||||
!matches!(dynamic, DynamicType::Divergent(_)),
|
||||
"DynamicType::Divergent should have been handled in an earlier branch"
|
||||
);
|
||||
ConstraintSet::from(match relation {
|
||||
ConstraintSet::from_bool(
|
||||
db,
|
||||
match relation {
|
||||
TypeRelation::Subtyping | TypeRelation::SubtypingAssuming(_) => false,
|
||||
TypeRelation::Assignability | TypeRelation::ConstraintSetAssignability => {
|
||||
true
|
||||
}
|
||||
TypeRelation::Redundancy => match target {
|
||||
Type::Dynamic(_) => true,
|
||||
Type::Union(union) => union.elements(db).iter().any(Type::is_dynamic),
|
||||
_ => false,
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
(_, Type::Dynamic(_)) => ConstraintSet::from_bool(
|
||||
db,
|
||||
match relation {
|
||||
TypeRelation::Subtyping | TypeRelation::SubtypingAssuming(_) => false,
|
||||
TypeRelation::Assignability | TypeRelation::ConstraintSetAssignability => true,
|
||||
TypeRelation::Redundancy => match target {
|
||||
TypeRelation::Redundancy => match self {
|
||||
Type::Dynamic(_) => true,
|
||||
Type::Union(union) => union.elements(db).iter().any(Type::is_dynamic),
|
||||
Type::Intersection(intersection) => {
|
||||
// If a `Divergent` type is involved, it must not be eliminated.
|
||||
intersection
|
||||
.positive(db)
|
||||
.iter()
|
||||
.any(Type::is_non_divergent_dynamic)
|
||||
}
|
||||
_ => false,
|
||||
},
|
||||
})
|
||||
}
|
||||
(_, Type::Dynamic(_)) => ConstraintSet::from(match relation {
|
||||
TypeRelation::Subtyping | TypeRelation::SubtypingAssuming(_) => false,
|
||||
TypeRelation::Assignability | TypeRelation::ConstraintSetAssignability => true,
|
||||
TypeRelation::Redundancy => match self {
|
||||
Type::Dynamic(_) => true,
|
||||
Type::Intersection(intersection) => {
|
||||
// If a `Divergent` type is involved, it must not be eliminated.
|
||||
intersection
|
||||
.positive(db)
|
||||
.iter()
|
||||
.any(Type::is_non_divergent_dynamic)
|
||||
}
|
||||
_ => false,
|
||||
},
|
||||
}),
|
||||
),
|
||||
|
||||
// In general, a TypeVar `T` is not a subtype of a type `S` unless one of the two conditions is satisfied:
|
||||
// 1. `T` is a bound TypeVar and `T`'s upper bound is a subtype of `S`.
|
||||
@@ -471,7 +479,7 @@ impl<'db> Type<'db> {
|
||||
if !bound_typevar.is_inferable(db, inferable)
|
||||
&& union.elements(db).contains(&self) =>
|
||||
{
|
||||
ConstraintSet::from(true)
|
||||
ConstraintSet::from_bool(db, true)
|
||||
}
|
||||
|
||||
// A similar rule applies in reverse to intersection types.
|
||||
@@ -479,13 +487,13 @@ impl<'db> Type<'db> {
|
||||
if !bound_typevar.is_inferable(db, inferable)
|
||||
&& intersection.positive(db).contains(&target) =>
|
||||
{
|
||||
ConstraintSet::from(true)
|
||||
ConstraintSet::from_bool(db, true)
|
||||
}
|
||||
(Type::Intersection(intersection), Type::TypeVar(bound_typevar))
|
||||
if !bound_typevar.is_inferable(db, inferable)
|
||||
&& intersection.negative(db).contains(&target) =>
|
||||
{
|
||||
ConstraintSet::from(false)
|
||||
ConstraintSet::from_bool(db, false)
|
||||
}
|
||||
|
||||
// Two identical typevars must always solve to the same type, so they are always
|
||||
@@ -497,7 +505,7 @@ impl<'db> Type<'db> {
|
||||
if !lhs_bound_typevar.is_inferable(db, inferable)
|
||||
&& lhs_bound_typevar.is_same_typevar_as(db, rhs_bound_typevar) =>
|
||||
{
|
||||
ConstraintSet::from(true)
|
||||
ConstraintSet::from_bool(db, true)
|
||||
}
|
||||
|
||||
// `type[T]` is a subtype of the class object `A` if every instance of `T` is a subtype of an instance
|
||||
@@ -506,7 +514,7 @@ impl<'db> Type<'db> {
|
||||
if !subclass_of
|
||||
.into_type_var()
|
||||
.zip(target.to_instance(db))
|
||||
.when_some_and(|(this_instance, other_instance)| {
|
||||
.when_some_and(db, |(this_instance, other_instance)| {
|
||||
Type::TypeVar(this_instance).has_relation_to_impl(
|
||||
db,
|
||||
other_instance,
|
||||
@@ -522,7 +530,7 @@ impl<'db> Type<'db> {
|
||||
subclass_of
|
||||
.into_type_var()
|
||||
.zip(target.to_instance(db))
|
||||
.when_some_and(|(this_instance, other_instance)| {
|
||||
.when_some_and(db, |(this_instance, other_instance)| {
|
||||
Type::TypeVar(this_instance).has_relation_to_impl(
|
||||
db,
|
||||
other_instance,
|
||||
@@ -538,7 +546,7 @@ impl<'db> Type<'db> {
|
||||
if !subclass_of
|
||||
.into_type_var()
|
||||
.zip(self.to_instance(db))
|
||||
.when_some_and(|(other_instance, this_instance)| {
|
||||
.when_some_and(db, |(other_instance, this_instance)| {
|
||||
this_instance.has_relation_to_impl(
|
||||
db,
|
||||
Type::TypeVar(other_instance),
|
||||
@@ -554,7 +562,7 @@ impl<'db> Type<'db> {
|
||||
subclass_of
|
||||
.into_type_var()
|
||||
.zip(self.to_instance(db))
|
||||
.when_some_and(|(other_instance, this_instance)| {
|
||||
.when_some_and(db, |(other_instance, this_instance)| {
|
||||
this_instance.has_relation_to_impl(
|
||||
db,
|
||||
Type::TypeVar(other_instance),
|
||||
@@ -607,7 +615,7 @@ impl<'db> Type<'db> {
|
||||
&& !bound_typevar
|
||||
.typevar(db)
|
||||
.constraints(db)
|
||||
.when_some_and(|constraints| {
|
||||
.when_some_and(db, |constraints| {
|
||||
constraints.iter().when_all(db, |constraint| {
|
||||
self.has_relation_to_impl(
|
||||
db,
|
||||
@@ -628,7 +636,7 @@ impl<'db> Type<'db> {
|
||||
bound_typevar
|
||||
.typevar(db)
|
||||
.constraints(db)
|
||||
.when_some_and(|constraints| {
|
||||
.when_some_and(db, |constraints| {
|
||||
constraints.iter().when_all(db, |constraint| {
|
||||
self.has_relation_to_impl(
|
||||
db,
|
||||
@@ -648,11 +656,11 @@ impl<'db> Type<'db> {
|
||||
|
||||
// TODO: record the unification constraints
|
||||
|
||||
ConstraintSet::from(true)
|
||||
ConstraintSet::from_bool(db, true)
|
||||
}
|
||||
|
||||
// `Never` is the bottom type, the empty set.
|
||||
(_, Type::Never) => ConstraintSet::from(false),
|
||||
(_, Type::Never) => ConstraintSet::from_bool(db, false),
|
||||
|
||||
(Type::NewTypeInstance(self_newtype), Type::NewTypeInstance(target_newtype)) => {
|
||||
self_newtype.has_relation_to_impl(db, target_newtype)
|
||||
@@ -711,7 +719,7 @@ impl<'db> Type<'db> {
|
||||
disjointness_visitor,
|
||||
)
|
||||
} else {
|
||||
ConstraintSet::from(false)
|
||||
ConstraintSet::from_bool(db, false)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -815,7 +823,7 @@ impl<'db> Type<'db> {
|
||||
// bound. This is true even if the bound is a final class, since the typevar can still
|
||||
// be specialized to `Never`.)
|
||||
(_, Type::TypeVar(bound_typevar)) if !bound_typevar.is_inferable(db, inferable) => {
|
||||
ConstraintSet::from(false)
|
||||
ConstraintSet::from_bool(db, false)
|
||||
}
|
||||
|
||||
(_, Type::TypeVar(typevar))
|
||||
@@ -836,26 +844,29 @@ impl<'db> Type<'db> {
|
||||
{
|
||||
// TODO: record the unification constraints
|
||||
|
||||
typevar.typevar(db).upper_bound(db).when_none_or(|bound| {
|
||||
self.has_relation_to_impl(
|
||||
db,
|
||||
bound,
|
||||
inferable,
|
||||
relation,
|
||||
relation_visitor,
|
||||
disjointness_visitor,
|
||||
)
|
||||
})
|
||||
typevar
|
||||
.typevar(db)
|
||||
.upper_bound(db)
|
||||
.when_none_or(db, |bound| {
|
||||
self.has_relation_to_impl(
|
||||
db,
|
||||
bound,
|
||||
inferable,
|
||||
relation,
|
||||
relation_visitor,
|
||||
disjointness_visitor,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: Infer specializations here
|
||||
(_, Type::TypeVar(bound_typevar)) if bound_typevar.is_inferable(db, inferable) => {
|
||||
ConstraintSet::from(false)
|
||||
ConstraintSet::from_bool(db, false)
|
||||
}
|
||||
(Type::TypeVar(bound_typevar), _) => {
|
||||
// All inferable cases should have been handled above
|
||||
assert!(!bound_typevar.is_inferable(db, inferable));
|
||||
ConstraintSet::from(false)
|
||||
ConstraintSet::from_bool(db, false)
|
||||
}
|
||||
|
||||
// All other `NewType` assignments fall back to the concrete base type.
|
||||
@@ -875,8 +886,12 @@ impl<'db> Type<'db> {
|
||||
|
||||
// Note that the definition of `Type::AlwaysFalsy` depends on the return value of `__bool__`.
|
||||
// If `__bool__` always returns True or False, it can be treated as a subtype of `AlwaysTruthy` or `AlwaysFalsy`, respectively.
|
||||
(left, Type::AlwaysFalsy) => ConstraintSet::from(left.bool(db).is_always_false()),
|
||||
(left, Type::AlwaysTruthy) => ConstraintSet::from(left.bool(db).is_always_true()),
|
||||
(left, Type::AlwaysFalsy) => {
|
||||
ConstraintSet::from_bool(db, left.bool(db).is_always_false())
|
||||
}
|
||||
(left, Type::AlwaysTruthy) => {
|
||||
ConstraintSet::from_bool(db, left.bool(db).is_always_true())
|
||||
}
|
||||
// Currently, the only supertype of `AlwaysFalsy` and `AlwaysTruthy` is the universal set (object instance).
|
||||
(Type::AlwaysFalsy | Type::AlwaysTruthy, _) => {
|
||||
target.when_equivalent_to(db, Type::object(), inferable)
|
||||
@@ -936,7 +951,7 @@ impl<'db> Type<'db> {
|
||||
| Type::FunctionLiteral(_)
|
||||
| Type::ModuleLiteral(_)
|
||||
| Type::EnumLiteral(_),
|
||||
) => ConstraintSet::from(false),
|
||||
) => ConstraintSet::from_bool(db, false),
|
||||
|
||||
(Type::Callable(self_callable), Type::Callable(other_callable)) => relation_visitor
|
||||
.visit((self, target, relation), || {
|
||||
@@ -952,16 +967,17 @@ impl<'db> Type<'db> {
|
||||
|
||||
(_, Type::Callable(other_callable)) => {
|
||||
relation_visitor.visit((self, target, relation), || {
|
||||
self.try_upcast_to_callable(db).when_some_and(|callables| {
|
||||
callables.has_relation_to_impl(
|
||||
db,
|
||||
other_callable,
|
||||
inferable,
|
||||
relation,
|
||||
relation_visitor,
|
||||
disjointness_visitor,
|
||||
)
|
||||
})
|
||||
self.try_upcast_to_callable(db)
|
||||
.when_some_and(db, |callables| {
|
||||
callables.has_relation_to_impl(
|
||||
db,
|
||||
other_callable,
|
||||
inferable,
|
||||
relation,
|
||||
relation_visitor,
|
||||
disjointness_visitor,
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -999,7 +1015,7 @@ impl<'db> Type<'db> {
|
||||
}
|
||||
|
||||
// A protocol instance can never be a subtype of a nominal type, with the *sole* exception of `object`.
|
||||
(Type::ProtocolInstance(_), _) => ConstraintSet::from(false),
|
||||
(Type::ProtocolInstance(_), _) => ConstraintSet::from_bool(db, false),
|
||||
|
||||
(Type::TypedDict(self_typeddict), Type::TypedDict(other_typeddict)) => relation_visitor
|
||||
.visit((self, target, relation), || {
|
||||
@@ -1031,22 +1047,22 @@ impl<'db> Type<'db> {
|
||||
}),
|
||||
|
||||
// A non-`TypedDict` cannot subtype a `TypedDict`
|
||||
(_, Type::TypedDict(_)) => ConstraintSet::from(false),
|
||||
(_, Type::TypedDict(_)) => ConstraintSet::from_bool(db, false),
|
||||
|
||||
// All `StringLiteral` types are a subtype of `LiteralString`.
|
||||
(Type::StringLiteral(_), Type::LiteralString) => ConstraintSet::from(true),
|
||||
(Type::StringLiteral(_), Type::LiteralString) => ConstraintSet::from_bool(db, true),
|
||||
|
||||
// An instance is a subtype of an enum literal, if it is an instance of the enum class
|
||||
// and the enum has only one member.
|
||||
(Type::NominalInstance(_), Type::EnumLiteral(target_enum_literal)) => {
|
||||
if target_enum_literal.enum_class_instance(db) != self {
|
||||
return ConstraintSet::from(false);
|
||||
return ConstraintSet::from_bool(db, false);
|
||||
}
|
||||
|
||||
ConstraintSet::from(is_single_member_enum(
|
||||
ConstraintSet::from_bool(
|
||||
db,
|
||||
target_enum_literal.enum_class(db),
|
||||
))
|
||||
is_single_member_enum(db, target_enum_literal.enum_class(db)),
|
||||
)
|
||||
}
|
||||
|
||||
// Except for the special `LiteralString` case above,
|
||||
@@ -1062,7 +1078,7 @@ impl<'db> Type<'db> {
|
||||
| Type::EnumLiteral(_)
|
||||
| Type::FunctionLiteral(_),
|
||||
_,
|
||||
) => (self.literal_fallback_instance(db)).when_some_and(|instance| {
|
||||
) => (self.literal_fallback_instance(db)).when_some_and(db, |instance| {
|
||||
instance.has_relation_to_impl(
|
||||
db,
|
||||
target,
|
||||
@@ -1107,7 +1123,7 @@ impl<'db> Type<'db> {
|
||||
|
||||
(Type::DataclassDecorator(_) | Type::DataclassTransformer(_), _) => {
|
||||
// TODO: Implement subtyping using an equivalent `Callable` type.
|
||||
ConstraintSet::from(false)
|
||||
ConstraintSet::from_bool(db, false)
|
||||
}
|
||||
|
||||
// `TypeIs` is invariant.
|
||||
@@ -1170,7 +1186,7 @@ impl<'db> Type<'db> {
|
||||
)
|
||||
}
|
||||
|
||||
(Type::Callable(_), _) => ConstraintSet::from(false),
|
||||
(Type::Callable(_), _) => ConstraintSet::from_bool(db, false),
|
||||
|
||||
(Type::BoundSuper(_), Type::BoundSuper(_)) => {
|
||||
self.when_equivalent_to(db, target, inferable)
|
||||
@@ -1187,7 +1203,7 @@ impl<'db> Type<'db> {
|
||||
(Type::SubclassOf(subclass_of), _) | (_, Type::SubclassOf(subclass_of))
|
||||
if subclass_of.is_type_var() =>
|
||||
{
|
||||
ConstraintSet::from(false)
|
||||
ConstraintSet::from_bool(db, false)
|
||||
}
|
||||
|
||||
// `Literal[<class 'C'>]` is a subtype of `type[B]` if `C` is a subclass of `B`,
|
||||
@@ -1205,7 +1221,7 @@ impl<'db> Type<'db> {
|
||||
disjointness_visitor,
|
||||
)
|
||||
})
|
||||
.unwrap_or_else(|| ConstraintSet::from(relation.is_assignability())),
|
||||
.unwrap_or_else(|| ConstraintSet::from_bool(db, relation.is_assignability())),
|
||||
|
||||
// Similarly, `<class 'C'>` is assignable to `<class 'C[...]'>` (a generic-alias type)
|
||||
// if the default specialization of `C` is assignable to `C[...]`. This scenario occurs
|
||||
@@ -1247,7 +1263,7 @@ impl<'db> Type<'db> {
|
||||
disjointness_visitor,
|
||||
)
|
||||
})
|
||||
.unwrap_or_else(|| ConstraintSet::from(relation.is_assignability())),
|
||||
.unwrap_or_else(|| ConstraintSet::from_bool(db, relation.is_assignability())),
|
||||
|
||||
// This branch asks: given two types `type[T]` and `type[S]`, is `type[T]` a subtype of `type[S]`?
|
||||
(Type::SubclassOf(self_subclass_ty), Type::SubclassOf(target_subclass_ty)) => {
|
||||
@@ -1298,7 +1314,7 @@ impl<'db> Type<'db> {
|
||||
disjointness_visitor,
|
||||
)
|
||||
.or(db, || {
|
||||
ConstraintSet::from(relation.is_assignability()).and(db, || {
|
||||
ConstraintSet::from_bool(db, relation.is_assignability()).and(db, || {
|
||||
other.has_relation_to_impl(
|
||||
db,
|
||||
KnownClass::Type.to_instance(db),
|
||||
@@ -1403,7 +1419,7 @@ impl<'db> Type<'db> {
|
||||
|
||||
// Other than the special cases enumerated above, nominal-instance types are never
|
||||
// subtypes of any other variants
|
||||
(Type::NominalInstance(_), _) => ConstraintSet::from(false),
|
||||
(Type::NominalInstance(_), _) => ConstraintSet::from_bool(db, false),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1430,7 +1446,7 @@ impl<'db> Type<'db> {
|
||||
other: Type<'db>,
|
||||
inferable: InferableTypeVars<'_, 'db>,
|
||||
) -> ConstraintSet<'db> {
|
||||
self.is_equivalent_to_impl(db, other, inferable, &IsEquivalentVisitor::default())
|
||||
self.is_equivalent_to_impl(db, other, inferable, &default_equivalent_visitor(db))
|
||||
}
|
||||
|
||||
pub(crate) fn is_equivalent_to_impl(
|
||||
@@ -1441,7 +1457,7 @@ impl<'db> Type<'db> {
|
||||
visitor: &IsEquivalentVisitor<'db>,
|
||||
) -> ConstraintSet<'db> {
|
||||
if self == other {
|
||||
return ConstraintSet::from(true);
|
||||
return ConstraintSet::from_bool(db, true);
|
||||
}
|
||||
|
||||
match (self, other) {
|
||||
@@ -1449,17 +1465,17 @@ impl<'db> Type<'db> {
|
||||
// which prevents `Divergent` from being eliminated during union reduction.
|
||||
(Type::Dynamic(_), Type::Dynamic(DynamicType::Divergent(_)))
|
||||
| (Type::Dynamic(DynamicType::Divergent(_)), Type::Dynamic(_)) => {
|
||||
ConstraintSet::from(false)
|
||||
ConstraintSet::from_bool(db, false)
|
||||
}
|
||||
(Type::Dynamic(_), Type::Dynamic(_)) => ConstraintSet::from(true),
|
||||
(Type::Dynamic(_), Type::Dynamic(_)) => ConstraintSet::from_bool(db, true),
|
||||
|
||||
(Type::SubclassOf(first), Type::SubclassOf(second)) => {
|
||||
match (first.subclass_of(), second.subclass_of()) {
|
||||
(first, second) if first == second => ConstraintSet::from(true),
|
||||
(first, second) if first == second => ConstraintSet::from_bool(db, true),
|
||||
(SubclassOfInner::Dynamic(_), SubclassOfInner::Dynamic(_)) => {
|
||||
ConstraintSet::from(true)
|
||||
ConstraintSet::from_bool(db, true)
|
||||
}
|
||||
_ => ConstraintSet::from(false),
|
||||
_ => ConstraintSet::from_bool(db, false),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1478,7 +1494,7 @@ impl<'db> Type<'db> {
|
||||
}
|
||||
|
||||
(Type::NewTypeInstance(self_newtype), Type::NewTypeInstance(other_newtype)) => {
|
||||
ConstraintSet::from(self_newtype.is_equivalent_to_impl(db, other_newtype))
|
||||
ConstraintSet::from_bool(db, self_newtype.is_equivalent_to_impl(db, other_newtype))
|
||||
}
|
||||
|
||||
(Type::NominalInstance(first), Type::NominalInstance(second)) => {
|
||||
@@ -1511,16 +1527,16 @@ impl<'db> Type<'db> {
|
||||
}
|
||||
(Type::ProtocolInstance(protocol), nominal @ Type::NominalInstance(n))
|
||||
| (nominal @ Type::NominalInstance(n), Type::ProtocolInstance(protocol)) => {
|
||||
ConstraintSet::from(n.is_object() && protocol.normalized(db) == nominal)
|
||||
ConstraintSet::from_bool(db, n.is_object() && 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 ConstraintSet::from(false);
|
||||
return ConstraintSet::from_bool(db, false);
|
||||
}
|
||||
ConstraintSet::from(is_single_member_enum(db, instance.class_literal(db)))
|
||||
ConstraintSet::from_bool(db, is_single_member_enum(db, instance.class_literal(db)))
|
||||
}
|
||||
|
||||
(Type::PropertyInstance(left), Type::PropertyInstance(right)) => {
|
||||
@@ -1531,7 +1547,7 @@ impl<'db> Type<'db> {
|
||||
left.is_equivalent_to_impl(db, right, inferable, visitor)
|
||||
}),
|
||||
|
||||
_ => ConstraintSet::from(false),
|
||||
_ => ConstraintSet::from_bool(db, false),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1565,8 +1581,8 @@ impl<'db> Type<'db> {
|
||||
db,
|
||||
other,
|
||||
inferable,
|
||||
&IsDisjointVisitor::default(),
|
||||
&HasRelationToVisitor::default(),
|
||||
&default_disjoint_visitor(db),
|
||||
&default_relation_visitor(db),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1591,7 +1607,7 @@ impl<'db> Type<'db> {
|
||||
.member(db, member.name())
|
||||
.place
|
||||
.ignore_possibly_undefined()
|
||||
.when_none_or(|attribute_type| {
|
||||
.when_none_or(db, |attribute_type| {
|
||||
member.has_disjoint_type_from(
|
||||
db,
|
||||
attribute_type,
|
||||
@@ -1604,9 +1620,9 @@ impl<'db> Type<'db> {
|
||||
}
|
||||
|
||||
match (self, other) {
|
||||
(Type::Never, _) | (_, Type::Never) => ConstraintSet::from(true),
|
||||
(Type::Never, _) | (_, Type::Never) => ConstraintSet::from_bool(db, true),
|
||||
|
||||
(Type::Dynamic(_), _) | (_, Type::Dynamic(_)) => ConstraintSet::from(false),
|
||||
(Type::Dynamic(_), _) | (_, Type::Dynamic(_)) => ConstraintSet::from_bool(db, false),
|
||||
|
||||
(Type::TypeAlias(alias), _) => {
|
||||
let self_alias_ty = alias.value_type(db);
|
||||
@@ -1659,7 +1675,7 @@ impl<'db> Type<'db> {
|
||||
if !subclass_of
|
||||
.into_type_var()
|
||||
.zip(other.to_instance(db))
|
||||
.when_none_or(|(this_instance, other_instance)| {
|
||||
.when_none_or(db, |(this_instance, other_instance)| {
|
||||
Type::TypeVar(this_instance).is_disjoint_from_impl(
|
||||
db,
|
||||
other_instance,
|
||||
@@ -1674,7 +1690,7 @@ impl<'db> Type<'db> {
|
||||
subclass_of
|
||||
.into_type_var()
|
||||
.zip(other.to_instance(db))
|
||||
.when_none_or(|(this_instance, other_instance)| {
|
||||
.when_none_or(db, |(this_instance, other_instance)| {
|
||||
Type::TypeVar(this_instance).is_disjoint_from_impl(
|
||||
db,
|
||||
other_instance,
|
||||
@@ -1693,7 +1709,7 @@ impl<'db> Type<'db> {
|
||||
if !self_bound_typevar.is_inferable(db, inferable)
|
||||
&& self_bound_typevar.is_same_typevar_as(db, other_bound_typevar) =>
|
||||
{
|
||||
ConstraintSet::from(false)
|
||||
ConstraintSet::from_bool(db, false)
|
||||
}
|
||||
|
||||
(tvar @ Type::TypeVar(bound_typevar), Type::Intersection(intersection))
|
||||
@@ -1701,7 +1717,7 @@ impl<'db> Type<'db> {
|
||||
if !bound_typevar.is_inferable(db, inferable)
|
||||
&& intersection.negative(db).contains(&tvar) =>
|
||||
{
|
||||
ConstraintSet::from(true)
|
||||
ConstraintSet::from_bool(db, true)
|
||||
}
|
||||
|
||||
// An unbounded typevar is never disjoint from any other type, since it might be
|
||||
@@ -1712,7 +1728,7 @@ impl<'db> Type<'db> {
|
||||
if !bound_typevar.is_inferable(db, inferable) =>
|
||||
{
|
||||
match bound_typevar.typevar(db).bound_or_constraints(db) {
|
||||
None => ConstraintSet::from(false),
|
||||
None => ConstraintSet::from_bool(db, false),
|
||||
Some(TypeVarBoundOrConstraints::UpperBound(bound)) => bound
|
||||
.is_disjoint_from_impl(
|
||||
db,
|
||||
@@ -1736,7 +1752,7 @@ impl<'db> Type<'db> {
|
||||
}
|
||||
|
||||
// TODO: Infer specializations here
|
||||
(Type::TypeVar(_), _) | (_, Type::TypeVar(_)) => ConstraintSet::from(false),
|
||||
(Type::TypeVar(_), _) | (_, Type::TypeVar(_)) => ConstraintSet::from_bool(db, false),
|
||||
|
||||
(Type::Union(union), other) | (other, Type::Union(union)) => {
|
||||
union.elements(db).iter().when_all(db, |e| {
|
||||
@@ -1841,7 +1857,7 @@ impl<'db> Type<'db> {
|
||||
| Type::ClassLiteral(..)
|
||||
| Type::SpecialForm(..)
|
||||
| Type::KnownInstance(..)),
|
||||
) => ConstraintSet::from(left != right),
|
||||
) => ConstraintSet::from_bool(db, left != right),
|
||||
|
||||
(
|
||||
Type::SubclassOf(_),
|
||||
@@ -1870,16 +1886,16 @@ impl<'db> Type<'db> {
|
||||
| Type::WrapperDescriptor(..)
|
||||
| Type::ModuleLiteral(..),
|
||||
Type::SubclassOf(_),
|
||||
) => ConstraintSet::from(true),
|
||||
) => ConstraintSet::from_bool(db, true),
|
||||
|
||||
(Type::AlwaysTruthy, ty) | (ty, Type::AlwaysTruthy) => {
|
||||
// `Truthiness::Ambiguous` may include `AlwaysTrue` as a subset, so it's not guaranteed to be disjoint.
|
||||
// Thus, they are only disjoint if `ty.bool() == AlwaysFalse`.
|
||||
ConstraintSet::from(ty.bool(db).is_always_false())
|
||||
ConstraintSet::from_bool(db, ty.bool(db).is_always_false())
|
||||
}
|
||||
(Type::AlwaysFalsy, ty) | (ty, Type::AlwaysFalsy) => {
|
||||
// Similarly, they are only disjoint if `ty.bool() == AlwaysTrue`.
|
||||
ConstraintSet::from(ty.bool(db).is_always_true())
|
||||
ConstraintSet::from_bool(db, ty.bool(db).is_always_true())
|
||||
}
|
||||
|
||||
(Type::ProtocolInstance(left), Type::ProtocolInstance(right)) => disjointness_visitor
|
||||
@@ -2011,7 +2027,7 @@ impl<'db> Type<'db> {
|
||||
disjointness_visitor,
|
||||
relation_visitor,
|
||||
),
|
||||
Place::Undefined => ConstraintSet::from(false),
|
||||
Place::Undefined => ConstraintSet::from_bool(db, false),
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -2020,26 +2036,29 @@ impl<'db> Type<'db> {
|
||||
(Type::SubclassOf(subclass_of_ty), _) | (_, Type::SubclassOf(subclass_of_ty))
|
||||
if subclass_of_ty.is_type_var() =>
|
||||
{
|
||||
ConstraintSet::from(true)
|
||||
ConstraintSet::from_bool(db, true)
|
||||
}
|
||||
|
||||
(Type::GenericAlias(left_alias), Type::GenericAlias(right_alias)) => {
|
||||
ConstraintSet::from(left_alias.origin(db) != right_alias.origin(db)).or(db, || {
|
||||
left_alias.specialization(db).is_disjoint_from_impl(
|
||||
db,
|
||||
right_alias.specialization(db),
|
||||
inferable,
|
||||
disjointness_visitor,
|
||||
relation_visitor,
|
||||
)
|
||||
})
|
||||
ConstraintSet::from_bool(db, left_alias.origin(db) != right_alias.origin(db)).or(
|
||||
db,
|
||||
|| {
|
||||
left_alias.specialization(db).is_disjoint_from_impl(
|
||||
db,
|
||||
right_alias.specialization(db),
|
||||
inferable,
|
||||
disjointness_visitor,
|
||||
relation_visitor,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
(Type::ClassLiteral(class_literal), other @ Type::GenericAlias(_))
|
||||
| (other @ Type::GenericAlias(_), Type::ClassLiteral(class_literal)) => class_literal
|
||||
.default_specialization(db)
|
||||
.into_generic_alias()
|
||||
.when_none_or(|alias| {
|
||||
.when_none_or(db, |alias| {
|
||||
other.is_disjoint_from_impl(
|
||||
db,
|
||||
Type::GenericAlias(alias),
|
||||
@@ -2052,8 +2071,9 @@ impl<'db> Type<'db> {
|
||||
(Type::SubclassOf(subclass_of_ty), Type::ClassLiteral(class_b))
|
||||
| (Type::ClassLiteral(class_b), Type::SubclassOf(subclass_of_ty)) => {
|
||||
match subclass_of_ty.subclass_of() {
|
||||
SubclassOfInner::Dynamic(_) => ConstraintSet::from(false),
|
||||
SubclassOfInner::Class(class_a) => ConstraintSet::from(
|
||||
SubclassOfInner::Dynamic(_) => ConstraintSet::from_bool(db, false),
|
||||
SubclassOfInner::Class(class_a) => ConstraintSet::from_bool(
|
||||
db,
|
||||
!class_a.could_exist_in_mro_of(db, ClassType::NonGeneric(class_b)),
|
||||
),
|
||||
SubclassOfInner::TypeVar(_) => unreachable!(),
|
||||
@@ -2063,8 +2083,9 @@ impl<'db> Type<'db> {
|
||||
(Type::SubclassOf(subclass_of_ty), Type::GenericAlias(alias_b))
|
||||
| (Type::GenericAlias(alias_b), Type::SubclassOf(subclass_of_ty)) => {
|
||||
match subclass_of_ty.subclass_of() {
|
||||
SubclassOfInner::Dynamic(_) => ConstraintSet::from(false),
|
||||
SubclassOfInner::Class(class_a) => ConstraintSet::from(
|
||||
SubclassOfInner::Dynamic(_) => ConstraintSet::from_bool(db, false),
|
||||
SubclassOfInner::Class(class_a) => ConstraintSet::from_bool(
|
||||
db,
|
||||
!class_a.could_exist_in_mro_of(db, ClassType::Generic(alias_b)),
|
||||
),
|
||||
SubclassOfInner::TypeVar(_) => unreachable!(),
|
||||
@@ -2102,12 +2123,12 @@ impl<'db> Type<'db> {
|
||||
|
||||
(Type::SpecialForm(special_form), Type::NominalInstance(instance))
|
||||
| (Type::NominalInstance(instance), Type::SpecialForm(special_form)) => {
|
||||
ConstraintSet::from(!special_form.is_instance_of(db, instance.class(db)))
|
||||
ConstraintSet::from_bool(db, !special_form.is_instance_of(db, instance.class(db)))
|
||||
}
|
||||
|
||||
(Type::KnownInstance(known_instance), Type::NominalInstance(instance))
|
||||
| (Type::NominalInstance(instance), Type::KnownInstance(known_instance)) => {
|
||||
ConstraintSet::from(!known_instance.is_instance_of(db, instance.class(db)))
|
||||
ConstraintSet::from_bool(db, !known_instance.is_instance_of(db, instance.class(db)))
|
||||
}
|
||||
|
||||
(
|
||||
@@ -2127,7 +2148,7 @@ impl<'db> Type<'db> {
|
||||
|
||||
(Type::BooleanLiteral(..) | Type::TypeIs(_) | Type::TypeGuard(_), _)
|
||||
| (_, Type::BooleanLiteral(..) | Type::TypeIs(_) | Type::TypeGuard(_)) => {
|
||||
ConstraintSet::from(true)
|
||||
ConstraintSet::from_bool(db, true)
|
||||
}
|
||||
|
||||
(Type::IntLiteral(..), Type::NominalInstance(instance))
|
||||
@@ -2139,10 +2160,12 @@ impl<'db> Type<'db> {
|
||||
.negate(db)
|
||||
}
|
||||
|
||||
(Type::IntLiteral(..), _) | (_, Type::IntLiteral(..)) => ConstraintSet::from(true),
|
||||
(Type::IntLiteral(..), _) | (_, Type::IntLiteral(..)) => {
|
||||
ConstraintSet::from_bool(db, true)
|
||||
}
|
||||
|
||||
(Type::StringLiteral(..), Type::LiteralString)
|
||||
| (Type::LiteralString, Type::StringLiteral(..)) => ConstraintSet::from(false),
|
||||
| (Type::LiteralString, Type::StringLiteral(..)) => ConstraintSet::from_bool(db, false),
|
||||
|
||||
(Type::StringLiteral(..) | Type::LiteralString, Type::NominalInstance(instance))
|
||||
| (Type::NominalInstance(instance), Type::StringLiteral(..) | Type::LiteralString) => {
|
||||
@@ -2153,8 +2176,10 @@ impl<'db> Type<'db> {
|
||||
.negate(db)
|
||||
}
|
||||
|
||||
(Type::LiteralString, Type::LiteralString) => ConstraintSet::from(false),
|
||||
(Type::LiteralString, _) | (_, Type::LiteralString) => ConstraintSet::from(true),
|
||||
(Type::LiteralString, Type::LiteralString) => ConstraintSet::from_bool(db, false),
|
||||
(Type::LiteralString, _) | (_, Type::LiteralString) => {
|
||||
ConstraintSet::from_bool(db, true)
|
||||
}
|
||||
|
||||
(Type::BytesLiteral(..), Type::NominalInstance(instance))
|
||||
| (Type::NominalInstance(instance), Type::BytesLiteral(..)) => {
|
||||
@@ -2179,7 +2204,9 @@ impl<'db> Type<'db> {
|
||||
)
|
||||
.negate(db)
|
||||
}
|
||||
(Type::EnumLiteral(..), _) | (_, Type::EnumLiteral(..)) => ConstraintSet::from(true),
|
||||
(Type::EnumLiteral(..), _) | (_, Type::EnumLiteral(..)) => {
|
||||
ConstraintSet::from_bool(db, true)
|
||||
}
|
||||
|
||||
// A class-literal type `X` is always disjoint from an instance type `Y`,
|
||||
// unless the type expressing "all instances of `Z`" is a subtype of of `Y`,
|
||||
@@ -2250,7 +2277,7 @@ impl<'db> Type<'db> {
|
||||
// No two callable types are ever disjoint because
|
||||
// `(*args: object, **kwargs: object) -> Never` is a subtype of all fully static
|
||||
// callable types.
|
||||
ConstraintSet::from(false)
|
||||
ConstraintSet::from_bool(db, false)
|
||||
}
|
||||
|
||||
(Type::Callable(_), Type::StringLiteral(_) | Type::BytesLiteral(_))
|
||||
@@ -2258,7 +2285,7 @@ impl<'db> Type<'db> {
|
||||
// A callable type is disjoint from other literal types. For example,
|
||||
// `Type::StringLiteral` must be an instance of exactly `str`, not a subclass
|
||||
// of `str`, and `str` is not callable. The same applies to other literal types.
|
||||
ConstraintSet::from(true)
|
||||
ConstraintSet::from_bool(db, true)
|
||||
}
|
||||
|
||||
(Type::Callable(_), Type::SpecialForm(special_form))
|
||||
@@ -2267,7 +2294,7 @@ impl<'db> Type<'db> {
|
||||
// that are callable (like TypedDict and collection constructors).
|
||||
// Most special forms are type constructors/annotations (like `typing.Literal`,
|
||||
// `typing.Union`, etc.) that are subscripted, not called.
|
||||
ConstraintSet::from(!special_form.is_callable())
|
||||
ConstraintSet::from_bool(db, !special_form.is_callable())
|
||||
}
|
||||
|
||||
(
|
||||
@@ -2285,7 +2312,7 @@ impl<'db> Type<'db> {
|
||||
)
|
||||
.place
|
||||
.ignore_possibly_undefined()
|
||||
.when_none_or(|dunder_call| {
|
||||
.when_none_or(db, |dunder_call| {
|
||||
dunder_call
|
||||
.has_relation_to_impl(
|
||||
db,
|
||||
@@ -2307,7 +2334,7 @@ impl<'db> Type<'db> {
|
||||
Type::Callable(_) | Type::DataclassDecorator(_) | Type::DataclassTransformer(_),
|
||||
) => {
|
||||
// TODO: Implement disjointness for general callable type with other types
|
||||
ConstraintSet::from(false)
|
||||
ConstraintSet::from_bool(db, false)
|
||||
}
|
||||
|
||||
(Type::ModuleLiteral(..), other @ Type::NominalInstance(..))
|
||||
@@ -2369,7 +2396,9 @@ impl<'db> Type<'db> {
|
||||
)
|
||||
}
|
||||
|
||||
(Type::GenericAlias(_), _) | (_, Type::GenericAlias(_)) => ConstraintSet::from(true),
|
||||
(Type::GenericAlias(_), _) | (_, Type::GenericAlias(_)) => {
|
||||
ConstraintSet::from_bool(db, true)
|
||||
}
|
||||
|
||||
(Type::TypedDict(self_typeddict), Type::TypedDict(other_typeddict)) => {
|
||||
disjointness_visitor.visit((self, other), || {
|
||||
@@ -2416,10 +2445,8 @@ fn is_redundant_with_cycle_initial<'db>(
|
||||
pub(crate) type HasRelationToVisitor<'db> =
|
||||
CycleDetector<TypeRelation<'db>, (Type<'db>, Type<'db>, TypeRelation<'db>), ConstraintSet<'db>>;
|
||||
|
||||
impl Default for HasRelationToVisitor<'_> {
|
||||
fn default() -> Self {
|
||||
HasRelationToVisitor::new(ConstraintSet::from(true))
|
||||
}
|
||||
pub(crate) fn default_relation_visitor(db: &dyn Db) -> HasRelationToVisitor<'_> {
|
||||
HasRelationToVisitor::new(ConstraintSet::from_bool(db, true))
|
||||
}
|
||||
|
||||
/// A [`PairVisitor`] that is used in `is_disjoint_from` methods.
|
||||
@@ -2428,10 +2455,8 @@ pub(crate) type IsDisjointVisitor<'db> = PairVisitor<'db, IsDisjoint, Constraint
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct IsDisjoint;
|
||||
|
||||
impl Default for IsDisjointVisitor<'_> {
|
||||
fn default() -> Self {
|
||||
IsDisjointVisitor::new(ConstraintSet::from(false))
|
||||
}
|
||||
pub(crate) fn default_disjoint_visitor(db: &dyn Db) -> IsDisjointVisitor<'_> {
|
||||
IsDisjointVisitor::new(ConstraintSet::from_bool(db, false))
|
||||
}
|
||||
|
||||
/// A [`PairVisitor`] that is used in `is_equivalent` methods.
|
||||
@@ -2440,8 +2465,6 @@ pub(crate) type IsEquivalentVisitor<'db> = PairVisitor<'db, IsEquivalent, Constr
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct IsEquivalent;
|
||||
|
||||
impl Default for IsEquivalentVisitor<'_> {
|
||||
fn default() -> Self {
|
||||
IsEquivalentVisitor::new(ConstraintSet::from(true))
|
||||
}
|
||||
pub(crate) fn default_equivalent_visitor(db: &dyn Db) -> IsEquivalentVisitor<'_> {
|
||||
IsEquivalentVisitor::new(ConstraintSet::from_bool(db, true))
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ use crate::types::generics::{GenericContext, InferableTypeVars, walk_generic_con
|
||||
use crate::types::infer::{infer_deferred_types, infer_scope_types};
|
||||
use crate::types::relation::{
|
||||
HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor, TypeRelation,
|
||||
default_disjoint_visitor, default_relation_visitor,
|
||||
};
|
||||
use crate::types::{
|
||||
ApplyTypeMappingVisitor, BindingContext, BoundTypeVarInstance, CallableType, CallableTypeKind,
|
||||
@@ -306,8 +307,8 @@ impl<'db> CallableSignature<'db> {
|
||||
other,
|
||||
inferable,
|
||||
TypeRelation::Subtyping,
|
||||
&HasRelationToVisitor::default(),
|
||||
&IsDisjointVisitor::default(),
|
||||
&default_relation_visitor(db),
|
||||
&default_disjoint_visitor(db),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -362,8 +363,8 @@ impl<'db> CallableSignature<'db> {
|
||||
other,
|
||||
inferable,
|
||||
TypeRelation::ConstraintSetAssignability,
|
||||
&HasRelationToVisitor::default(),
|
||||
&IsDisjointVisitor::default(),
|
||||
&default_relation_visitor(db),
|
||||
&default_disjoint_visitor(db),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -549,7 +550,7 @@ impl<'db> CallableSignature<'db> {
|
||||
}
|
||||
(_, _) => {
|
||||
if self == other {
|
||||
return ConstraintSet::from(true);
|
||||
return ConstraintSet::from_bool(db, true);
|
||||
}
|
||||
self.is_subtype_of_impl(db, other, inferable)
|
||||
.and(db, || other.is_subtype_of_impl(db, self, inferable))
|
||||
@@ -1005,14 +1006,14 @@ impl<'db> Signature<'db> {
|
||||
inferable: InferableTypeVars<'_, 'db>,
|
||||
visitor: &IsEquivalentVisitor<'db>,
|
||||
) -> ConstraintSet<'db> {
|
||||
let mut result = ConstraintSet::from(true);
|
||||
let mut result = ConstraintSet::from_bool(db, true);
|
||||
|
||||
if self.parameters.is_gradual() != other.parameters.is_gradual() {
|
||||
return ConstraintSet::from(false);
|
||||
return ConstraintSet::from_bool(db, false);
|
||||
}
|
||||
|
||||
if self.parameters.len() != other.parameters.len() {
|
||||
return ConstraintSet::from(false);
|
||||
return ConstraintSet::from_bool(db, false);
|
||||
}
|
||||
|
||||
let mut check_types = |self_type: Type<'db>, other_type: Type<'db>| {
|
||||
@@ -1069,7 +1070,7 @@ impl<'db> Signature<'db> {
|
||||
|
||||
(ParameterKind::KeywordVariadic { .. }, ParameterKind::KeywordVariadic { .. }) => {}
|
||||
|
||||
_ => return ConstraintSet::from(false),
|
||||
_ => return ConstraintSet::from_bool(db, false),
|
||||
}
|
||||
|
||||
if !check_types(
|
||||
@@ -1132,8 +1133,8 @@ impl<'db> Signature<'db> {
|
||||
other,
|
||||
inferable,
|
||||
TypeRelation::ConstraintSetAssignability,
|
||||
&HasRelationToVisitor::default(),
|
||||
&IsDisjointVisitor::default(),
|
||||
&default_relation_visitor(db),
|
||||
&default_disjoint_visitor(db),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1251,7 +1252,7 @@ impl<'db> Signature<'db> {
|
||||
}
|
||||
}
|
||||
|
||||
let mut result = ConstraintSet::from(true);
|
||||
let mut result = ConstraintSet::from_bool(db, true);
|
||||
|
||||
let mut check_types = |type1: Type<'db>, type2: Type<'db>| {
|
||||
match (type1, type2) {
|
||||
@@ -1308,15 +1309,15 @@ impl<'db> Signature<'db> {
|
||||
.keyword_variadic()
|
||||
.is_some_and(|(_, param)| param.annotated_type().is_object())
|
||||
{
|
||||
return ConstraintSet::from(true);
|
||||
return ConstraintSet::from_bool(db, true);
|
||||
}
|
||||
|
||||
// The top signature is supertype of (and assignable from) all other signatures. It is a
|
||||
// subtype of no signature except itself, and assignable only to the gradual signature.
|
||||
if other.parameters.is_top() {
|
||||
return ConstraintSet::from(true);
|
||||
return ConstraintSet::from_bool(db, true);
|
||||
} else if self.parameters.is_top() && !other.parameters.is_gradual() {
|
||||
return ConstraintSet::from(false);
|
||||
return ConstraintSet::from_bool(db, false);
|
||||
}
|
||||
|
||||
// If either of the parameter lists is gradual (`...`), then it is assignable to and from
|
||||
@@ -1324,7 +1325,8 @@ impl<'db> Signature<'db> {
|
||||
if self.parameters.is_gradual() || other.parameters.is_gradual() {
|
||||
result.intersect(
|
||||
db,
|
||||
ConstraintSet::from(
|
||||
ConstraintSet::from_bool(
|
||||
db,
|
||||
relation.is_assignability() || relation.is_constraint_set_assignability(),
|
||||
),
|
||||
);
|
||||
@@ -1426,7 +1428,7 @@ impl<'db> Signature<'db> {
|
||||
// `other`, then the non-variadic parameters in `self` must have a default
|
||||
// value.
|
||||
if default_type.is_none() {
|
||||
return ConstraintSet::from(false);
|
||||
return ConstraintSet::from_bool(db, false);
|
||||
}
|
||||
}
|
||||
ParameterKind::Variadic { .. } | ParameterKind::KeywordVariadic { .. } => {
|
||||
@@ -1438,7 +1440,7 @@ impl<'db> Signature<'db> {
|
||||
EitherOrBoth::Right(_) => {
|
||||
// If there are more parameters in `other` than in `self`, then `self` is not a
|
||||
// subtype of `other`.
|
||||
return ConstraintSet::from(false);
|
||||
return ConstraintSet::from_bool(db, false);
|
||||
}
|
||||
|
||||
EitherOrBoth::Both(self_parameter, other_parameter) => {
|
||||
@@ -1458,7 +1460,7 @@ impl<'db> Signature<'db> {
|
||||
},
|
||||
) => {
|
||||
if self_default.is_none() && other_default.is_some() {
|
||||
return ConstraintSet::from(false);
|
||||
return ConstraintSet::from_bool(db, false);
|
||||
}
|
||||
if !check_types(
|
||||
other_parameter.annotated_type(),
|
||||
@@ -1479,11 +1481,11 @@ impl<'db> Signature<'db> {
|
||||
},
|
||||
) => {
|
||||
if self_name != other_name {
|
||||
return ConstraintSet::from(false);
|
||||
return ConstraintSet::from_bool(db, false);
|
||||
}
|
||||
// The following checks are the same as positional-only parameters.
|
||||
if self_default.is_none() && other_default.is_some() {
|
||||
return ConstraintSet::from(false);
|
||||
return ConstraintSet::from_bool(db, false);
|
||||
}
|
||||
if !check_types(
|
||||
other_parameter.annotated_type(),
|
||||
@@ -1568,7 +1570,7 @@ impl<'db> Signature<'db> {
|
||||
break;
|
||||
}
|
||||
|
||||
_ => return ConstraintSet::from(false),
|
||||
_ => return ConstraintSet::from_bool(db, false),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1601,7 +1603,7 @@ impl<'db> Signature<'db> {
|
||||
// previous loop. They cannot be matched against any parameter in `other` which
|
||||
// only contains keyword-only and keyword-variadic parameters so the subtype
|
||||
// relation is invalid.
|
||||
return ConstraintSet::from(false);
|
||||
return ConstraintSet::from_bool(db, false);
|
||||
}
|
||||
ParameterKind::Variadic { .. } => {}
|
||||
}
|
||||
@@ -1628,7 +1630,7 @@ impl<'db> Signature<'db> {
|
||||
..
|
||||
} => {
|
||||
if self_default.is_none() && other_default.is_some() {
|
||||
return ConstraintSet::from(false);
|
||||
return ConstraintSet::from_bool(db, false);
|
||||
}
|
||||
if !check_types(
|
||||
other_parameter.annotated_type(),
|
||||
@@ -1649,14 +1651,14 @@ impl<'db> Signature<'db> {
|
||||
return result;
|
||||
}
|
||||
} else {
|
||||
return ConstraintSet::from(false);
|
||||
return ConstraintSet::from_bool(db, false);
|
||||
}
|
||||
}
|
||||
ParameterKind::KeywordVariadic { .. } => {
|
||||
let Some(self_keyword_variadic_type) = self_keyword_variadic else {
|
||||
// For a `self <: other` relationship, if `other` has a keyword variadic
|
||||
// parameter, `self` must also have a keyword variadic parameter.
|
||||
return ConstraintSet::from(false);
|
||||
return ConstraintSet::from_bool(db, false);
|
||||
};
|
||||
if !check_types(other_parameter.annotated_type(), self_keyword_variadic_type) {
|
||||
return result;
|
||||
@@ -1664,7 +1666,7 @@ impl<'db> Signature<'db> {
|
||||
}
|
||||
_ => {
|
||||
// This can only occur in case of a syntax error.
|
||||
return ConstraintSet::from(false);
|
||||
return ConstraintSet::from_bool(db, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1673,7 +1675,7 @@ impl<'db> Signature<'db> {
|
||||
// optional otherwise the subtype relation is invalid.
|
||||
for (_, self_parameter) in self_keywords {
|
||||
if self_parameter.default_type().is_none() {
|
||||
return ConstraintSet::from(false);
|
||||
return ConstraintSet::from_bool(db, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -224,13 +224,16 @@ impl<'db> SubclassOfType<'db> {
|
||||
) -> ConstraintSet<'db> {
|
||||
match (self.subclass_of, other.subclass_of) {
|
||||
(SubclassOfInner::Dynamic(_), SubclassOfInner::Dynamic(_)) => {
|
||||
ConstraintSet::from(!relation.is_subtyping())
|
||||
ConstraintSet::from_bool(db, !relation.is_subtyping())
|
||||
}
|
||||
(SubclassOfInner::Dynamic(_), SubclassOfInner::Class(other_class)) => {
|
||||
ConstraintSet::from(other_class.is_object(db) || relation.is_assignability())
|
||||
ConstraintSet::from_bool(
|
||||
db,
|
||||
other_class.is_object(db) || relation.is_assignability(),
|
||||
)
|
||||
}
|
||||
(SubclassOfInner::Class(_), SubclassOfInner::Dynamic(_)) => {
|
||||
ConstraintSet::from(relation.is_assignability())
|
||||
ConstraintSet::from_bool(db, relation.is_assignability())
|
||||
}
|
||||
|
||||
// For example, `type[bool]` describes all possible runtime subclasses of the class `bool`,
|
||||
@@ -264,10 +267,10 @@ impl<'db> SubclassOfType<'db> {
|
||||
) -> ConstraintSet<'db> {
|
||||
match (self.subclass_of, other.subclass_of) {
|
||||
(SubclassOfInner::Dynamic(_), _) | (_, SubclassOfInner::Dynamic(_)) => {
|
||||
ConstraintSet::from(false)
|
||||
ConstraintSet::from_bool(db, false)
|
||||
}
|
||||
(SubclassOfInner::Class(self_class), SubclassOfInner::Class(other_class)) => {
|
||||
ConstraintSet::from(!self_class.could_coexist_in_mro_with(db, other_class))
|
||||
ConstraintSet::from_bool(db, !self_class.could_coexist_in_mro_with(db, other_class))
|
||||
}
|
||||
(SubclassOfInner::TypeVar(_), _) | (_, SubclassOfInner::TypeVar(_)) => {
|
||||
unreachable!()
|
||||
|
||||
@@ -516,7 +516,7 @@ impl<'db> FixedLengthTuple<Type<'db>> {
|
||||
) -> ConstraintSet<'db> {
|
||||
match other {
|
||||
Tuple::Fixed(other) => {
|
||||
ConstraintSet::from(self.0.len() == other.0.len()).and(db, || {
|
||||
ConstraintSet::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.has_relation_to_impl(
|
||||
db,
|
||||
@@ -533,11 +533,11 @@ impl<'db> FixedLengthTuple<Type<'db>> {
|
||||
Tuple::Variable(other) => {
|
||||
// This tuple must have enough elements to match up with the other tuple's prefix
|
||||
// and suffix, and each of those elements must pairwise satisfy the relation.
|
||||
let mut result = ConstraintSet::from(true);
|
||||
let mut result = ConstraintSet::from_bool(db, true);
|
||||
let mut self_iter = self.0.iter();
|
||||
for other_ty in other.prefix_elements() {
|
||||
let Some(self_ty) = self_iter.next() else {
|
||||
return ConstraintSet::from(false);
|
||||
return ConstraintSet::from_bool(db, false);
|
||||
};
|
||||
let element_constraints = self_ty.has_relation_to_impl(
|
||||
db,
|
||||
@@ -556,7 +556,7 @@ impl<'db> FixedLengthTuple<Type<'db>> {
|
||||
}
|
||||
for other_ty in other.iter_suffix_elements().rev() {
|
||||
let Some(self_ty) = self_iter.next_back() else {
|
||||
return ConstraintSet::from(false);
|
||||
return ConstraintSet::from_bool(db, false);
|
||||
};
|
||||
let element_constraints = self_ty.has_relation_to_impl(
|
||||
db,
|
||||
@@ -599,7 +599,7 @@ impl<'db> FixedLengthTuple<Type<'db>> {
|
||||
inferable: InferableTypeVars<'_, 'db>,
|
||||
visitor: &IsEquivalentVisitor<'db>,
|
||||
) -> ConstraintSet<'db> {
|
||||
ConstraintSet::from(self.0.len() == other.0.len()).and(db, || {
|
||||
ConstraintSet::from_bool(db, self.0.len() == other.0.len()).and(db, || {
|
||||
(self.0.iter())
|
||||
.zip(&other.0)
|
||||
.when_all(db, |(self_ty, other_ty)| {
|
||||
@@ -1031,17 +1031,17 @@ impl<'db> VariableLengthTuple<Type<'db>> {
|
||||
// possible lengths. This means that `tuple[Any, ...]` can match any tuple of any
|
||||
// length.
|
||||
if !relation.is_assignability() || !self.variable().is_dynamic() {
|
||||
return ConstraintSet::from(false);
|
||||
return ConstraintSet::from_bool(db, false);
|
||||
}
|
||||
|
||||
// In addition, the other tuple must have enough elements to match up with this
|
||||
// tuple's prefix and suffix, and each of those elements must pairwise satisfy the
|
||||
// relation.
|
||||
let mut result = ConstraintSet::from(true);
|
||||
let mut result = ConstraintSet::from_bool(db, true);
|
||||
let mut other_iter = other.iter_all_elements();
|
||||
for self_ty in self.prenormalized_prefix_elements(db, None) {
|
||||
let Some(other_ty) = other_iter.next() else {
|
||||
return ConstraintSet::from(false);
|
||||
return ConstraintSet::from_bool(db, false);
|
||||
};
|
||||
let element_constraints = self_ty.has_relation_to_impl(
|
||||
db,
|
||||
@@ -1061,7 +1061,7 @@ impl<'db> VariableLengthTuple<Type<'db>> {
|
||||
let suffix: Vec<_> = self.prenormalized_suffix_elements(db, None).collect();
|
||||
for self_ty in suffix.iter().rev() {
|
||||
let Some(other_ty) = other_iter.next_back() else {
|
||||
return ConstraintSet::from(false);
|
||||
return ConstraintSet::from_bool(db, false);
|
||||
};
|
||||
let element_constraints = self_ty.has_relation_to_impl(
|
||||
db,
|
||||
@@ -1097,7 +1097,7 @@ impl<'db> VariableLengthTuple<Type<'db>> {
|
||||
// The overlapping parts of the prefixes and suffixes must satisfy the relation.
|
||||
// Any remaining parts must satisfy the relation with the other tuple's
|
||||
// variable-length part.
|
||||
let mut result = ConstraintSet::from(true);
|
||||
let mut result = ConstraintSet::from_bool(db, true);
|
||||
let pairwise = self
|
||||
.prenormalized_prefix_elements(db, self_prenormalize_variable)
|
||||
.zip_longest(
|
||||
@@ -1127,7 +1127,7 @@ impl<'db> VariableLengthTuple<Type<'db>> {
|
||||
// that can materialize to provide it (for assignability only),
|
||||
// as in `tuple[Any, ...]` matching `tuple[int, int]`.
|
||||
if !relation.is_assignability() || !self.variable().is_dynamic() {
|
||||
return ConstraintSet::from(false);
|
||||
return ConstraintSet::from_bool(db, false);
|
||||
}
|
||||
self.variable().has_relation_to_impl(
|
||||
db,
|
||||
@@ -1181,7 +1181,7 @@ impl<'db> VariableLengthTuple<Type<'db>> {
|
||||
// that can materialize to provide it (for assignability only),
|
||||
// as in `tuple[Any, ...]` matching `tuple[int, int]`.
|
||||
if !relation.is_assignability() || !self.variable().is_dynamic() {
|
||||
return ConstraintSet::from(false);
|
||||
return ConstraintSet::from_bool(db, false);
|
||||
}
|
||||
self.variable().has_relation_to_impl(
|
||||
db,
|
||||
@@ -1233,7 +1233,7 @@ impl<'db> VariableLengthTuple<Type<'db>> {
|
||||
self_ty.is_equivalent_to_impl(db, other_ty, inferable, visitor)
|
||||
}
|
||||
EitherOrBoth::Left(_) | EitherOrBoth::Right(_) => {
|
||||
ConstraintSet::from(false)
|
||||
ConstraintSet::from_bool(db, false)
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -1245,7 +1245,7 @@ impl<'db> VariableLengthTuple<Type<'db>> {
|
||||
self_ty.is_equivalent_to_impl(db, other_ty, inferable, visitor)
|
||||
}
|
||||
EitherOrBoth::Left(_) | EitherOrBoth::Right(_) => {
|
||||
ConstraintSet::from(false)
|
||||
ConstraintSet::from_bool(db, false)
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -1513,7 +1513,7 @@ impl<'db> Tuple<Type<'db>> {
|
||||
self_tuple.is_equivalent_to_impl(db, other_tuple, inferable, visitor)
|
||||
}
|
||||
(Tuple::Fixed(_), Tuple::Variable(_)) | (Tuple::Variable(_), Tuple::Fixed(_)) => {
|
||||
ConstraintSet::from(false)
|
||||
ConstraintSet::from_bool(db, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1530,10 +1530,10 @@ impl<'db> Tuple<Type<'db>> {
|
||||
let (self_min, self_max) = self.len().size_hint();
|
||||
let (other_min, other_max) = other.len().size_hint();
|
||||
if self_max.is_some_and(|max| max < other_min) {
|
||||
return ConstraintSet::from(true);
|
||||
return ConstraintSet::from_bool(db, true);
|
||||
}
|
||||
if other_max.is_some_and(|max| max < self_min) {
|
||||
return ConstraintSet::from(true);
|
||||
return ConstraintSet::from_bool(db, true);
|
||||
}
|
||||
|
||||
// If any of the required elements are pairwise disjoint, the tuples are disjoint as well.
|
||||
|
||||
@@ -151,24 +151,24 @@ impl<'db> TypedDictType<'db> {
|
||||
&& let Some(target_defining_class) = target.defining_class()
|
||||
&& defining_class.is_subclass_of(db, target_defining_class)
|
||||
{
|
||||
return ConstraintSet::from(true);
|
||||
return ConstraintSet::from_bool(db, true);
|
||||
}
|
||||
|
||||
let self_items = self.items(db);
|
||||
let target_items = target.items(db);
|
||||
// Many rules violations short-circuit with "never", but asking whether one field is
|
||||
// [relation] to/of another can produce more complicated constraints, and we collect those.
|
||||
let mut constraints = ConstraintSet::from(true);
|
||||
let mut constraints = ConstraintSet::from_bool(db, true);
|
||||
for (target_item_name, target_item_field) in target_items {
|
||||
let field_constraints = if target_item_field.is_required() {
|
||||
// required target fields
|
||||
let Some(self_item_field) = self_items.get(target_item_name) else {
|
||||
// Self is missing a required field.
|
||||
return ConstraintSet::from(false);
|
||||
return ConstraintSet::from_bool(db, false);
|
||||
};
|
||||
if !self_item_field.is_required() {
|
||||
// A required field is not required in self.
|
||||
return ConstraintSet::from(false);
|
||||
return ConstraintSet::from_bool(db, false);
|
||||
}
|
||||
if target_item_field.is_read_only() {
|
||||
// For `ReadOnly[]` fields in the target, the corresponding fields in
|
||||
@@ -186,7 +186,7 @@ impl<'db> TypedDictType<'db> {
|
||||
} else {
|
||||
if self_item_field.is_read_only() {
|
||||
// A read-only field can't be assigned to a mutable target.
|
||||
return ConstraintSet::from(false);
|
||||
return ConstraintSet::from_bool(db, false);
|
||||
}
|
||||
// For mutable fields in the target, the relation needs to apply both
|
||||
// ways, or else mutating the target could violate the structural
|
||||
@@ -252,12 +252,12 @@ impl<'db> TypedDictType<'db> {
|
||||
if let Some(self_item_field) = self_items.get(target_item_name) {
|
||||
if self_item_field.is_read_only() {
|
||||
// A read-only field can't be assigned to a mutable target.
|
||||
return ConstraintSet::from(false);
|
||||
return ConstraintSet::from_bool(db, false);
|
||||
}
|
||||
if self_item_field.is_required() {
|
||||
// A required field can't be assigned to a not-required, mutable field
|
||||
// in the target, because `del` is allowed on the target field.
|
||||
return ConstraintSet::from(false);
|
||||
return ConstraintSet::from_bool(db, false);
|
||||
}
|
||||
|
||||
// As above, for mutable fields in the target, the relation needs
|
||||
@@ -289,7 +289,7 @@ impl<'db> TypedDictType<'db> {
|
||||
// interaction between two structural assignability rules prevents
|
||||
// unsoundness" in `typed_dict.md`.
|
||||
// TODO: `closed` and `extra_items` support will go here.
|
||||
ConstraintSet::from(false)
|
||||
ConstraintSet::from_bool(db, false)
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -341,13 +341,13 @@ impl<'db> TypedDictType<'db> {
|
||||
// sorted order instead of paying for a lookup for each field, as long as their lengths are
|
||||
// the same.
|
||||
if self.items(db).len() != other.items(db).len() {
|
||||
return ConstraintSet::from(false);
|
||||
return ConstraintSet::from_bool(db, false);
|
||||
}
|
||||
self.items(db).iter().zip(other.items(db)).when_all(
|
||||
db,
|
||||
|((name, field), (other_name, other_field))| {
|
||||
if name != other_name || field.flags != other_field.flags {
|
||||
return ConstraintSet::from(false);
|
||||
return ConstraintSet::from_bool(db, false);
|
||||
}
|
||||
field.declared_ty.is_equivalent_to_impl(
|
||||
db,
|
||||
@@ -435,7 +435,7 @@ impl<'db> TypedDictType<'db> {
|
||||
{
|
||||
// One side demands a `Required` source field, while the other side demands a
|
||||
// `NotRequired` one. They must be disjoint.
|
||||
return ConstraintSet::from(true);
|
||||
return ConstraintSet::from_bool(db, true);
|
||||
}
|
||||
}
|
||||
if !self_field.is_read_only() && !other_field.is_read_only() {
|
||||
|
||||
Reference in New Issue
Block a user