diff --git a/crates/ty_python_semantic/src/types.rs b/crates/ty_python_semantic/src/types.rs index 0082e3401a..0d41d88eb2 100644 --- a/crates/ty_python_semantic/src/types.rs +++ b/crates/ty_python_semantic/src/types.rs @@ -1627,12 +1627,11 @@ impl<'db> Type<'db> { db: &'db dyn Db, target: Type<'db>, constraints: ConstraintSet<'db>, - inferable: InferableTypeVars<'db>, ) -> ConstraintSet<'db> { self.has_relation_to( db, target, - inferable, + constraints.inferable, TypeRelation::ConstraintImplication(constraints), ) } diff --git a/crates/ty_python_semantic/src/types/call/bind.rs b/crates/ty_python_semantic/src/types/call/bind.rs index 1314f94230..08c4bf80b1 100644 --- a/crates/ty_python_semantic/src/types/call/bind.rs +++ b/crates/ty_python_semantic/src/types/call/bind.rs @@ -1142,7 +1142,13 @@ impl<'db> Bindings<'db> { else { return; }; - let constraints = ConstraintSet::range(db, *lower, *typevar, *upper); + let constraints = ConstraintSet::range( + db, + *lower, + *typevar, + *upper, + InferableTypeVars::none(), + ); let tracked = TrackedConstraintSet::new(db, constraints); overload.set_return_type(Type::KnownInstance( KnownInstanceType::ConstraintSet(tracked), @@ -1178,12 +1184,7 @@ impl<'db> Bindings<'db> { continue; }; - let result = ty_a.when_subtype_of_given( - db, - *ty_b, - tracked.constraints(db), - InferableTypeVars::none(), - ); + let result = ty_a.when_subtype_of_given(db, *ty_b, tracked.constraints(db)); let tracked = TrackedConstraintSet::new(db, result); overload.set_return_type(Type::KnownInstance( KnownInstanceType::ConstraintSet(tracked), @@ -1241,7 +1242,8 @@ impl<'db> Bindings<'db> { let result = tracked .constraints(db) - .satisfied_by_all_typevars(db, inferable); + .with_inferable(inferable) + .satisfied_by_all_typevars(db); overload.set_return_type(Type::BooleanLiteral(result)); } diff --git a/crates/ty_python_semantic/src/types/constraints.rs b/crates/ty_python_semantic/src/types/constraints.rs index 10f9008ed1..a163edb9dc 100644 --- a/crates/ty_python_semantic/src/types/constraints.rs +++ b/crates/ty_python_semantic/src/types/constraints.rs @@ -164,18 +164,23 @@ where pub struct ConstraintSet<'db> { /// The BDD representing this constraint set node: Node<'db>, + + /// The typevars that were inferable when constructing this constraint set + pub(crate) inferable: InferableTypeVars<'db>, } impl<'db> ConstraintSet<'db> { fn never() -> Self { Self { node: Node::AlwaysFalse, + inferable: InferableTypeVars::none(), } } fn always() -> Self { Self { node: Node::AlwaysTrue, + inferable: InferableTypeVars::none(), } } @@ -185,6 +190,7 @@ impl<'db> ConstraintSet<'db> { typevar: BoundTypeVarInstance<'db>, lower: Type<'db>, upper: Type<'db>, + inferable: InferableTypeVars<'db>, relation: TypeRelation<'db>, ) -> Self { let (lower, upper) = match relation { @@ -202,6 +208,14 @@ impl<'db> ConstraintSet<'db> { Self { node: ConstrainedTypeVar::new_node(db, typevar, lower, upper), + inferable, + } + } + + pub(crate) fn with_inferable(self, inferable: InferableTypeVars<'db>) -> Self { + Self { + node: self.node, + inferable, } } @@ -226,6 +240,7 @@ impl<'db> ConstraintSet<'db> { ) -> Self { Self { node: self.node.when_subtype_of_given(db, lhs, rhs), + inferable: self.inferable, } } @@ -243,22 +258,20 @@ impl<'db> ConstraintSet<'db> { /// since the constraint set cannot be affected by any typevars that it does not mention. That /// means that those additional typevars trivially satisfy the constraint set, regardless of /// whether they are inferable or not. - pub(crate) fn satisfied_by_all_typevars( - self, - db: &'db dyn Db, - inferable: InferableTypeVars<'db>, - ) -> bool { - self.node.satisfied_by_all_typevars(db, inferable) + pub(crate) fn satisfied_by_all_typevars(self, db: &'db dyn Db) -> bool { + self.node.satisfied_by_all_typevars(db, self.inferable) } /// Updates this constraint set to hold the union of itself and another constraint set. pub(crate) fn union(&mut self, db: &'db dyn Db, other: Self) -> Self { + debug_assert!(self.inferable == other.inferable); self.node = self.node.or(db, other.node); *self } /// Updates this constraint set to hold the intersection of itself and another constraint set. pub(crate) fn intersect(&mut self, db: &'db dyn Db, other: Self) -> Self { + debug_assert!(self.inferable == other.inferable); self.node = self.node.and(db, other.node); *self } @@ -267,6 +280,7 @@ impl<'db> ConstraintSet<'db> { pub(crate) fn negate(self, db: &'db dyn Db) -> Self { Self { node: self.node.negate(db), + inferable: self.inferable, } } @@ -296,8 +310,10 @@ impl<'db> ConstraintSet<'db> { } pub(crate) fn iff(self, db: &'db dyn Db, other: Self) -> Self { + debug_assert!(self.inferable == other.inferable); ConstraintSet { node: self.node.iff(db, other.node), + inferable: self.inferable, } } @@ -306,8 +322,16 @@ impl<'db> ConstraintSet<'db> { lower: Type<'db>, typevar: BoundTypeVarInstance<'db>, upper: Type<'db>, + inferable: InferableTypeVars<'db>, ) -> Self { - Self::constrain_typevar(db, typevar, lower, upper, TypeRelation::Assignability) + Self::constrain_typevar( + db, + typevar, + lower, + upper, + inferable, + TypeRelation::Assignability, + ) } pub(crate) fn display(self, db: &'db dyn Db) -> impl Display { @@ -2106,10 +2130,10 @@ mod tests { let u = BoundTypeVarInstance::synthetic(&db, "U", TypeVarVariance::Invariant); let bool_type = KnownClass::Bool.to_instance(&db); let str_type = KnownClass::Str.to_instance(&db); - let t_str = ConstraintSet::range(&db, str_type, t, str_type); - let t_bool = ConstraintSet::range(&db, bool_type, t, bool_type); - let u_str = ConstraintSet::range(&db, str_type, u, str_type); - let u_bool = ConstraintSet::range(&db, bool_type, u, bool_type); + let t_str = ConstraintSet::range(&db, str_type, t, str_type, InferableTypeVars::none()); + let t_bool = ConstraintSet::range(&db, bool_type, t, bool_type, InferableTypeVars::none()); + let u_str = ConstraintSet::range(&db, str_type, u, str_type, InferableTypeVars::none()); + let u_bool = ConstraintSet::range(&db, bool_type, u, bool_type, InferableTypeVars::none()); let constraints = (t_str.or(&db, || t_bool)).and(&db, || u_str.or(&db, || u_bool)); let actual = constraints.node.display_graph(&db, &"").to_string(); assert_eq!(actual, expected); diff --git a/crates/ty_python_semantic/src/types/generics.rs b/crates/ty_python_semantic/src/types/generics.rs index 0f78e417c8..7d56c1ac3f 100644 --- a/crates/ty_python_semantic/src/types/generics.rs +++ b/crates/ty_python_semantic/src/types/generics.rs @@ -119,7 +119,7 @@ pub(crate) fn typing_self<'db>( .map(Type::TypeVar) } -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, get_size2::GetSize, salsa::Update)] pub(crate) struct InferableTypeVars<'db> { inner: Option>, } @@ -167,6 +167,7 @@ impl<'db> InferableTypeVars<'db> { (None, None) => self, (Some(_), None) => self, (None, Some(_)) => other, + (Some(self_inner), Some(other_inner)) if self_inner == other_inner => self, (Some(self_inner), Some(other_inner)) => InferableTypeVars { inner: Some(self_inner.merge(db, other_inner)), },