mirror of https://github.com/astral-sh/ruff
[ty] Allow gradual lower/upper bounds in a constraint set (#21957)
We now allow the lower and upper bounds of a constraint to be gradual. Before, we would take the top/bottom materializations of the bounds. This required us to pass in whether the constraint was intended for a subtyping check or an assignability check, since that would control whether we took the "restrictive" or "permissive" materializations, respectively. Unfortunately, doing so means that we lost information about whether the original query involves a non-fully-static type. This would cause us to create specializations like `T = object` for the constraint `T ≤ Any`, when it would be nicer to carry through the gradual type and produce `T = Any`. We're not currently using constraint sets for subtyping checks, nor are we going to in the very near future. So for now, we're going to assume that constraint sets are always used for assignability checks, and allow the lower/upper bounds to not be fully static. Once we get to the point where we need to use constraint sets for subtyping checks, we will consider how best to record this information in constraints.
This commit is contained in:
parent
e19c050386
commit
b413a6dec4
|
|
@ -16,7 +16,7 @@ An unbounded typevar can specialize to any type. We will specialize the typevar
|
|||
bound of all of the types that satisfy the constraint set.
|
||||
|
||||
```py
|
||||
from typing import Never
|
||||
from typing import Any, Never
|
||||
from ty_extensions import ConstraintSet, generic_context
|
||||
|
||||
# fmt: off
|
||||
|
|
@ -26,6 +26,8 @@ def unbounded[T]():
|
|||
reveal_type(generic_context(unbounded).specialize_constrained(ConstraintSet.always()))
|
||||
# revealed: ty_extensions.Specialization[T@unbounded = object]
|
||||
reveal_type(generic_context(unbounded).specialize_constrained(ConstraintSet.range(Never, T, object)))
|
||||
# revealed: ty_extensions.Specialization[T@unbounded = Any]
|
||||
reveal_type(generic_context(unbounded).specialize_constrained(ConstraintSet.range(Never, T, Any)))
|
||||
# revealed: None
|
||||
reveal_type(generic_context(unbounded).specialize_constrained(ConstraintSet.never()))
|
||||
|
||||
|
|
@ -68,6 +70,10 @@ class Unrelated: ...
|
|||
def bounded[T: Base]():
|
||||
# revealed: ty_extensions.Specialization[T@bounded = Base]
|
||||
reveal_type(generic_context(bounded).specialize_constrained(ConstraintSet.always()))
|
||||
# revealed: ty_extensions.Specialization[T@bounded = Base]
|
||||
reveal_type(generic_context(bounded).specialize_constrained(ConstraintSet.range(Never, T, object)))
|
||||
# revealed: ty_extensions.Specialization[T@bounded = Base & Any]
|
||||
reveal_type(generic_context(bounded).specialize_constrained(ConstraintSet.range(Never, T, Any)))
|
||||
# revealed: None
|
||||
reveal_type(generic_context(bounded).specialize_constrained(ConstraintSet.never()))
|
||||
|
||||
|
|
@ -94,11 +100,17 @@ def bounded_by_gradual[T: Any]():
|
|||
# TODO: revealed: ty_extensions.Specialization[T@bounded_by_gradual = Any]
|
||||
# revealed: ty_extensions.Specialization[T@bounded_by_gradual = object]
|
||||
reveal_type(generic_context(bounded_by_gradual).specialize_constrained(ConstraintSet.always()))
|
||||
# revealed: ty_extensions.Specialization[T@bounded_by_gradual = object]
|
||||
reveal_type(generic_context(bounded_by_gradual).specialize_constrained(ConstraintSet.range(Never, T, object)))
|
||||
# revealed: ty_extensions.Specialization[T@bounded_by_gradual = Any]
|
||||
reveal_type(generic_context(bounded_by_gradual).specialize_constrained(ConstraintSet.range(Never, T, Any)))
|
||||
# revealed: None
|
||||
reveal_type(generic_context(bounded_by_gradual).specialize_constrained(ConstraintSet.never()))
|
||||
|
||||
# revealed: ty_extensions.Specialization[T@bounded_by_gradual = Base]
|
||||
reveal_type(generic_context(bounded_by_gradual).specialize_constrained(ConstraintSet.range(Never, T, Base)))
|
||||
# revealed: ty_extensions.Specialization[T@bounded_by_gradual = object]
|
||||
reveal_type(generic_context(bounded_by_gradual).specialize_constrained(ConstraintSet.range(Base, T, object)))
|
||||
|
||||
# revealed: ty_extensions.Specialization[T@bounded_by_gradual = Unrelated]
|
||||
reveal_type(generic_context(bounded_by_gradual).specialize_constrained(ConstraintSet.range(Never, T, Unrelated)))
|
||||
|
|
@ -106,14 +118,24 @@ def bounded_by_gradual[T: Any]():
|
|||
def bounded_by_gradual_list[T: list[Any]]():
|
||||
# revealed: ty_extensions.Specialization[T@bounded_by_gradual_list = Top[list[Any]]]
|
||||
reveal_type(generic_context(bounded_by_gradual_list).specialize_constrained(ConstraintSet.always()))
|
||||
# revealed: ty_extensions.Specialization[T@bounded_by_gradual_list = list[object]]
|
||||
reveal_type(generic_context(bounded_by_gradual_list).specialize_constrained(ConstraintSet.range(Never, T, list[object])))
|
||||
# revealed: ty_extensions.Specialization[T@bounded_by_gradual_list = list[Any]]
|
||||
reveal_type(generic_context(bounded_by_gradual_list).specialize_constrained(ConstraintSet.range(Never, T, list[Any])))
|
||||
# revealed: None
|
||||
reveal_type(generic_context(bounded_by_gradual_list).specialize_constrained(ConstraintSet.never()))
|
||||
|
||||
# revealed: ty_extensions.Specialization[T@bounded_by_gradual_list = list[Base]]
|
||||
reveal_type(generic_context(bounded_by_gradual_list).specialize_constrained(ConstraintSet.range(Never, T, list[Base])))
|
||||
# TODO: revealed: ty_extensions.Specialization[T@bounded_by_gradual_list = list[Base]]
|
||||
# revealed: ty_extensions.Specialization[T@bounded_by_gradual_list = Top[list[Any]]]
|
||||
reveal_type(generic_context(bounded_by_gradual_list).specialize_constrained(ConstraintSet.range(list[Base], T, object)))
|
||||
|
||||
# revealed: ty_extensions.Specialization[T@bounded_by_gradual_list = list[Unrelated]]
|
||||
reveal_type(generic_context(bounded_by_gradual_list).specialize_constrained(ConstraintSet.range(Never, T, list[Unrelated])))
|
||||
# TODO: revealed: ty_extensions.Specialization[T@bounded_by_gradual_list = list[Unrelated]]
|
||||
# revealed: ty_extensions.Specialization[T@bounded_by_gradual_list = Top[list[Any]]]
|
||||
reveal_type(generic_context(bounded_by_gradual_list).specialize_constrained(ConstraintSet.range(list[Unrelated], T, object)))
|
||||
```
|
||||
|
||||
## Constrained typevar
|
||||
|
|
@ -142,12 +164,21 @@ def constrained[T: (Base, Unrelated)]():
|
|||
# revealed: None
|
||||
reveal_type(generic_context(constrained).specialize_constrained(ConstraintSet.always()))
|
||||
# revealed: None
|
||||
reveal_type(generic_context(constrained).specialize_constrained(ConstraintSet.range(Never, T, object)))
|
||||
# revealed: None
|
||||
reveal_type(generic_context(constrained).specialize_constrained(ConstraintSet.range(Never, T, Any)))
|
||||
# revealed: None
|
||||
reveal_type(generic_context(constrained).specialize_constrained(ConstraintSet.never()))
|
||||
|
||||
# revealed: ty_extensions.Specialization[T@constrained = Base]
|
||||
reveal_type(generic_context(constrained).specialize_constrained(ConstraintSet.range(Never, T, Base)))
|
||||
# revealed: ty_extensions.Specialization[T@constrained = Base]
|
||||
reveal_type(generic_context(constrained).specialize_constrained(ConstraintSet.range(Base, T, object)))
|
||||
|
||||
# revealed: ty_extensions.Specialization[T@constrained = Unrelated]
|
||||
reveal_type(generic_context(constrained).specialize_constrained(ConstraintSet.range(Never, T, Unrelated)))
|
||||
# revealed: ty_extensions.Specialization[T@constrained = Unrelated]
|
||||
reveal_type(generic_context(constrained).specialize_constrained(ConstraintSet.range(Unrelated, T, object)))
|
||||
|
||||
# revealed: ty_extensions.Specialization[T@constrained = Base]
|
||||
reveal_type(generic_context(constrained).specialize_constrained(ConstraintSet.range(Never, T, Super)))
|
||||
|
|
@ -178,15 +209,25 @@ def constrained_by_gradual[T: (Base, Any)]():
|
|||
# TODO: revealed: ty_extensions.Specialization[T@constrained_by_gradual = Any]
|
||||
# revealed: ty_extensions.Specialization[T@constrained_by_gradual = Base]
|
||||
reveal_type(generic_context(constrained_by_gradual).specialize_constrained(ConstraintSet.range(Never, T, object)))
|
||||
# TODO: revealed: ty_extensions.Specialization[T@constrained_by_gradual = Any]
|
||||
# revealed: ty_extensions.Specialization[T@constrained_by_gradual = Base & Any]
|
||||
reveal_type(generic_context(constrained_by_gradual).specialize_constrained(ConstraintSet.range(Never, T, Any)))
|
||||
# revealed: None
|
||||
reveal_type(generic_context(constrained_by_gradual).specialize_constrained(ConstraintSet.never()))
|
||||
|
||||
# TODO: revealed: ty_extensions.Specialization[T@constrained_by_gradual = Any]
|
||||
# revealed: ty_extensions.Specialization[T@constrained_by_gradual = Base]
|
||||
reveal_type(generic_context(constrained_by_gradual).specialize_constrained(ConstraintSet.range(Never, T, Base)))
|
||||
# TODO: revealed: ty_extensions.Specialization[T@constrained_by_gradual = Any]
|
||||
# revealed: ty_extensions.Specialization[T@constrained_by_gradual = Base]
|
||||
reveal_type(generic_context(constrained_by_gradual).specialize_constrained(ConstraintSet.range(Base, T, object)))
|
||||
|
||||
# TODO: revealed: ty_extensions.Specialization[T@constrained_by_gradual = Any]
|
||||
# revealed: ty_extensions.Specialization[T@constrained_by_gradual = Unrelated]
|
||||
reveal_type(generic_context(constrained_by_gradual).specialize_constrained(ConstraintSet.range(Never, T, Unrelated)))
|
||||
# TODO: revealed: ty_extensions.Specialization[T@constrained_by_gradual = Any]
|
||||
# revealed: ty_extensions.Specialization[T@constrained_by_gradual = object]
|
||||
reveal_type(generic_context(constrained_by_gradual).specialize_constrained(ConstraintSet.range(Unrelated, T, object)))
|
||||
|
||||
# TODO: revealed: ty_extensions.Specialization[T@constrained_by_gradual = Any]
|
||||
# revealed: ty_extensions.Specialization[T@constrained_by_gradual = Base]
|
||||
|
|
@ -206,6 +247,11 @@ def constrained_by_two_gradual[T: (Any, Any)]():
|
|||
# TODO: revealed: ty_extensions.Specialization[T@constrained_by_gradual = Any]
|
||||
# revealed: ty_extensions.Specialization[T@constrained_by_two_gradual = object]
|
||||
reveal_type(generic_context(constrained_by_two_gradual).specialize_constrained(ConstraintSet.always()))
|
||||
# TODO: revealed: ty_extensions.Specialization[T@constrained_by_two_gradual = Any]
|
||||
# revealed: ty_extensions.Specialization[T@constrained_by_two_gradual = object]
|
||||
reveal_type(generic_context(constrained_by_two_gradual).specialize_constrained(ConstraintSet.range(Never, T, object)))
|
||||
# revealed: ty_extensions.Specialization[T@constrained_by_two_gradual = Any]
|
||||
reveal_type(generic_context(constrained_by_two_gradual).specialize_constrained(ConstraintSet.range(Never, T, Any)))
|
||||
# revealed: None
|
||||
reveal_type(generic_context(constrained_by_two_gradual).specialize_constrained(ConstraintSet.never()))
|
||||
|
||||
|
|
@ -233,14 +279,24 @@ def constrained_by_two_gradual[T: (Any, Any)]():
|
|||
def constrained_by_gradual_list[T: (list[Base], list[Any])]():
|
||||
# revealed: ty_extensions.Specialization[T@constrained_by_gradual_list = list[Base]]
|
||||
reveal_type(generic_context(constrained_by_gradual_list).specialize_constrained(ConstraintSet.always()))
|
||||
# revealed: ty_extensions.Specialization[T@constrained_by_gradual_list = list[object]]
|
||||
reveal_type(generic_context(constrained_by_gradual_list).specialize_constrained(ConstraintSet.range(Never, T, list[object])))
|
||||
# TODO: revealed: ty_extensions.Specialization[T@constrained_by_gradual_list = list[Any]]
|
||||
# revealed: ty_extensions.Specialization[T@constrained_by_gradual_list = list[Base] & list[Any]]
|
||||
reveal_type(generic_context(constrained_by_gradual_list).specialize_constrained(ConstraintSet.range(Never, T, list[Any])))
|
||||
# revealed: None
|
||||
reveal_type(generic_context(constrained_by_gradual_list).specialize_constrained(ConstraintSet.never()))
|
||||
|
||||
# revealed: ty_extensions.Specialization[T@constrained_by_gradual_list = list[Base]]
|
||||
reveal_type(generic_context(constrained_by_gradual_list).specialize_constrained(ConstraintSet.range(Never, T, list[Base])))
|
||||
# TODO: revealed: ty_extensions.Specialization[T@constrained_by_gradual = list[Any]]
|
||||
# revealed: ty_extensions.Specialization[T@constrained_by_gradual_list = list[Base]]
|
||||
reveal_type(generic_context(constrained_by_gradual_list).specialize_constrained(ConstraintSet.range(list[Base], T, object)))
|
||||
|
||||
# revealed: ty_extensions.Specialization[T@constrained_by_gradual_list = list[Unrelated]]
|
||||
reveal_type(generic_context(constrained_by_gradual_list).specialize_constrained(ConstraintSet.range(Never, T, list[Unrelated])))
|
||||
# TODO: revealed: ty_extensions.Specialization[T@constrained_by_gradual = list[Unrelated]]
|
||||
# revealed: ty_extensions.Specialization[T@constrained_by_gradual_list = Top[list[Any]]]
|
||||
reveal_type(generic_context(constrained_by_gradual_list).specialize_constrained(ConstraintSet.range(list[Unrelated], T, object)))
|
||||
|
||||
# TODO: revealed: ty_extensions.Specialization[T@constrained_by_gradual = list[Any]]
|
||||
# revealed: ty_extensions.Specialization[T@constrained_by_gradual_list = list[Super]]
|
||||
|
|
@ -257,14 +313,25 @@ def constrained_by_gradual_list[T: (list[Base], list[Any])]():
|
|||
def constrained_by_gradual_list_reverse[T: (list[Any], list[Base])]():
|
||||
# revealed: ty_extensions.Specialization[T@constrained_by_gradual_list_reverse = list[Base]]
|
||||
reveal_type(generic_context(constrained_by_gradual_list_reverse).specialize_constrained(ConstraintSet.always()))
|
||||
# revealed: ty_extensions.Specialization[T@constrained_by_gradual_list_reverse = list[object]]
|
||||
reveal_type(generic_context(constrained_by_gradual_list_reverse).specialize_constrained(ConstraintSet.range(Never, T, list[object])))
|
||||
# TODO: revealed: ty_extensions.Specialization[T@constrained_by_gradual_list_reverse = list[Any]]
|
||||
# revealed: ty_extensions.Specialization[T@constrained_by_gradual_list_reverse = list[Base] & list[Any]]
|
||||
reveal_type(generic_context(constrained_by_gradual_list_reverse).specialize_constrained(ConstraintSet.range(Never, T, list[Any])))
|
||||
# revealed: None
|
||||
reveal_type(generic_context(constrained_by_gradual_list_reverse).specialize_constrained(ConstraintSet.never()))
|
||||
|
||||
# revealed: ty_extensions.Specialization[T@constrained_by_gradual_list_reverse = list[Base]]
|
||||
reveal_type(generic_context(constrained_by_gradual_list_reverse).specialize_constrained(ConstraintSet.range(Never, T, list[Base])))
|
||||
# revealed: ty_extensions.Specialization[T@constrained_by_gradual_list_reverse = list[Base]]
|
||||
reveal_type(generic_context(constrained_by_gradual_list_reverse).specialize_constrained(ConstraintSet.range(list[Base], T, object)))
|
||||
|
||||
# TODO: revealed: ty_extensions.Specialization[T@constrained_by_gradual = list[Any]]
|
||||
# revealed: ty_extensions.Specialization[T@constrained_by_gradual_list_reverse = list[Unrelated]]
|
||||
reveal_type(generic_context(constrained_by_gradual_list_reverse).specialize_constrained(ConstraintSet.range(Never, T, list[Unrelated])))
|
||||
# TODO: revealed: ty_extensions.Specialization[T@constrained_by_gradual = list[Unrelated]]
|
||||
# revealed: ty_extensions.Specialization[T@constrained_by_gradual_list_reverse = Top[list[Any]]]
|
||||
reveal_type(generic_context(constrained_by_gradual_list_reverse).specialize_constrained(ConstraintSet.range(list[Unrelated], T, object)))
|
||||
|
||||
# TODO: revealed: ty_extensions.Specialization[T@constrained_by_gradual = list[Any]]
|
||||
# revealed: ty_extensions.Specialization[T@constrained_by_gradual_list_reverse = list[Super]]
|
||||
|
|
@ -280,15 +347,26 @@ def constrained_by_two_gradual_lists[T: (list[Any], list[Any])]():
|
|||
# TODO: revealed: ty_extensions.Specialization[T@constrained_by_gradual = list[Any]]
|
||||
# revealed: ty_extensions.Specialization[T@constrained_by_two_gradual_lists = Top[list[Any]]]
|
||||
reveal_type(generic_context(constrained_by_two_gradual_lists).specialize_constrained(ConstraintSet.always()))
|
||||
# TODO: revealed: ty_extensions.Specialization[T@constrained_by_two_gradual_lists = list[Any]]
|
||||
# revealed: ty_extensions.Specialization[T@constrained_by_two_gradual_lists = Top[list[Any]]]
|
||||
reveal_type(generic_context(constrained_by_two_gradual_lists).specialize_constrained(ConstraintSet.range(Never, T, object)))
|
||||
# revealed: ty_extensions.Specialization[T@constrained_by_two_gradual_lists = list[Any]]
|
||||
reveal_type(generic_context(constrained_by_two_gradual_lists).specialize_constrained(ConstraintSet.range(Never, T, list[Any])))
|
||||
# revealed: None
|
||||
reveal_type(generic_context(constrained_by_two_gradual_lists).specialize_constrained(ConstraintSet.never()))
|
||||
|
||||
# TODO: revealed: ty_extensions.Specialization[T@constrained_by_gradual = list[Any]]
|
||||
# revealed: ty_extensions.Specialization[T@constrained_by_two_gradual_lists = list[Base]]
|
||||
reveal_type(generic_context(constrained_by_two_gradual_lists).specialize_constrained(ConstraintSet.range(Never, T, list[Base])))
|
||||
# TODO: revealed: ty_extensions.Specialization[T@constrained_by_gradual = list[Base]]
|
||||
# revealed: ty_extensions.Specialization[T@constrained_by_two_gradual_lists = Top[list[Any]]]
|
||||
reveal_type(generic_context(constrained_by_two_gradual_lists).specialize_constrained(ConstraintSet.range(list[Base], T, object)))
|
||||
|
||||
# TODO: revealed: ty_extensions.Specialization[T@constrained_by_gradual = list[Any]]
|
||||
# revealed: ty_extensions.Specialization[T@constrained_by_two_gradual_lists = list[Unrelated]]
|
||||
reveal_type(generic_context(constrained_by_two_gradual_lists).specialize_constrained(ConstraintSet.range(Never, T, list[Unrelated])))
|
||||
# TODO: revealed: ty_extensions.Specialization[T@constrained_by_gradual = list[Unrelated]]
|
||||
# revealed: ty_extensions.Specialization[T@constrained_by_two_gradual_lists = Top[list[Any]]]
|
||||
reveal_type(generic_context(constrained_by_two_gradual_lists).specialize_constrained(ConstraintSet.range(list[Unrelated], T, object)))
|
||||
|
||||
# TODO: revealed: ty_extensions.Specialization[T@constrained_by_gradual = list[Any]]
|
||||
# revealed: ty_extensions.Specialization[T@constrained_by_two_gradual_lists = list[Super]]
|
||||
|
|
|
|||
|
|
@ -668,6 +668,7 @@ pub(crate) struct IntersectionBuilder<'db> {
|
|||
// but if a union is added to the intersection, we'll distribute ourselves over that union and
|
||||
// create a union of intersections.
|
||||
intersections: Vec<InnerIntersectionBuilder<'db>>,
|
||||
order_elements: bool,
|
||||
db: &'db dyn Db,
|
||||
}
|
||||
|
||||
|
|
@ -675,6 +676,7 @@ impl<'db> IntersectionBuilder<'db> {
|
|||
pub(crate) fn new(db: &'db dyn Db) -> Self {
|
||||
Self {
|
||||
db,
|
||||
order_elements: false,
|
||||
intersections: vec![InnerIntersectionBuilder::default()],
|
||||
}
|
||||
}
|
||||
|
|
@ -682,14 +684,25 @@ impl<'db> IntersectionBuilder<'db> {
|
|||
fn empty(db: &'db dyn Db) -> Self {
|
||||
Self {
|
||||
db,
|
||||
order_elements: false,
|
||||
intersections: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn order_elements(mut self, val: bool) -> Self {
|
||||
self.order_elements = val;
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fn add_positive(self, ty: Type<'db>) -> Self {
|
||||
self.add_positive_impl(ty, &mut vec![])
|
||||
}
|
||||
|
||||
pub(crate) fn add_positive_in_place(&mut self, ty: Type<'db>) {
|
||||
let updated = std::mem::replace(self, Self::empty(self.db)).add_positive(ty);
|
||||
*self = updated;
|
||||
}
|
||||
|
||||
pub(crate) fn add_positive_impl(
|
||||
mut self,
|
||||
ty: Type<'db>,
|
||||
|
|
@ -898,13 +911,16 @@ impl<'db> IntersectionBuilder<'db> {
|
|||
pub(crate) fn build(mut self) -> Type<'db> {
|
||||
// Avoid allocating the UnionBuilder unnecessarily if we have just one intersection:
|
||||
if self.intersections.len() == 1 {
|
||||
self.intersections.pop().unwrap().build(self.db)
|
||||
self.intersections
|
||||
.pop()
|
||||
.unwrap()
|
||||
.build(self.db, self.order_elements)
|
||||
} else {
|
||||
UnionType::from_elements(
|
||||
self.db,
|
||||
self.intersections
|
||||
.into_iter()
|
||||
.map(|inner| inner.build(self.db)),
|
||||
.map(|inner| inner.build(self.db, self.order_elements)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1238,7 +1254,7 @@ impl<'db> InnerIntersectionBuilder<'db> {
|
|||
}
|
||||
}
|
||||
|
||||
fn build(mut self, db: &'db dyn Db) -> Type<'db> {
|
||||
fn build(mut self, db: &'db dyn Db, order_elements: bool) -> Type<'db> {
|
||||
self.simplify_constrained_typevars(db);
|
||||
|
||||
// If any typevars are in `self.positive`, speculatively solve all bounded type variables
|
||||
|
|
@ -1279,6 +1295,12 @@ impl<'db> InnerIntersectionBuilder<'db> {
|
|||
_ => {
|
||||
self.positive.shrink_to_fit();
|
||||
self.negative.shrink_to_fit();
|
||||
if order_elements {
|
||||
self.positive
|
||||
.sort_unstable_by(|l, r| union_or_intersection_elements_ordering(db, l, r));
|
||||
self.negative
|
||||
.sort_unstable_by(|l, r| union_or_intersection_elements_ordering(db, l, r));
|
||||
}
|
||||
Type::Intersection(IntersectionType::new(db, self.positive, self.negative))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,8 +78,8 @@ use salsa::plumbing::AsId;
|
|||
use crate::types::generics::{GenericContext, InferableTypeVars, Specialization};
|
||||
use crate::types::visitor::{TypeCollector, TypeVisitor, walk_type_with_recursion_guard};
|
||||
use crate::types::{
|
||||
BoundTypeVarIdentity, BoundTypeVarInstance, IntersectionType, Type, TypeRelation,
|
||||
TypeVarBoundOrConstraints, UnionType, walk_bound_type_var_type,
|
||||
BoundTypeVarIdentity, BoundTypeVarInstance, IntersectionBuilder, IntersectionType, Type,
|
||||
TypeVarBoundOrConstraints, UnionBuilder, UnionType, walk_bound_type_var_type,
|
||||
};
|
||||
use crate::{Db, FxOrderSet};
|
||||
|
||||
|
|
@ -198,21 +198,7 @@ impl<'db> ConstraintSet<'db> {
|
|||
typevar: BoundTypeVarInstance<'db>,
|
||||
lower: Type<'db>,
|
||||
upper: Type<'db>,
|
||||
relation: TypeRelation<'db>,
|
||||
) -> Self {
|
||||
let (lower, upper) = match relation {
|
||||
TypeRelation::Subtyping
|
||||
| TypeRelation::Redundancy
|
||||
| TypeRelation::SubtypingAssuming(_) => (
|
||||
lower.top_materialization(db),
|
||||
upper.bottom_materialization(db),
|
||||
),
|
||||
TypeRelation::Assignability => (
|
||||
lower.bottom_materialization(db),
|
||||
upper.top_materialization(db),
|
||||
),
|
||||
};
|
||||
|
||||
Self {
|
||||
node: ConstrainedTypeVar::new_node(db, typevar, lower, upper),
|
||||
}
|
||||
|
|
@ -428,7 +414,7 @@ impl<'db> ConstraintSet<'db> {
|
|||
typevar: BoundTypeVarInstance<'db>,
|
||||
upper: Type<'db>,
|
||||
) -> Self {
|
||||
Self::constrain_typevar(db, typevar, lower, upper, TypeRelation::Assignability)
|
||||
Self::constrain_typevar(db, typevar, lower, upper)
|
||||
}
|
||||
|
||||
#[expect(dead_code)] // Keep this around for debugging purposes
|
||||
|
|
@ -499,9 +485,6 @@ impl<'db> ConstrainedTypeVar<'db> {
|
|||
mut lower: Type<'db>,
|
||||
mut upper: Type<'db>,
|
||||
) -> Node<'db> {
|
||||
debug_assert_eq!(lower, lower.bottom_materialization(db));
|
||||
debug_assert_eq!(upper, upper.top_materialization(db));
|
||||
|
||||
// It's not useful for an upper bound to be an intersection type, or for a lower bound to
|
||||
// be a union type. Because the following equivalences hold, we can break these bounds
|
||||
// apart and create an equivalent BDD with more nodes but simpler constraints. (Fewer,
|
||||
|
|
@ -594,7 +577,7 @@ impl<'db> ConstrainedTypeVar<'db> {
|
|||
|
||||
// If `lower ≰ upper`, then the constraint cannot be satisfied, since there is no type that
|
||||
// is both greater than `lower`, and less than `upper`.
|
||||
if !lower.is_subtype_of(db, upper) {
|
||||
if !lower.is_assignable_to(db, upper) {
|
||||
return Node::AlwaysFalse;
|
||||
}
|
||||
|
||||
|
|
@ -712,8 +695,8 @@ impl<'db> ConstrainedTypeVar<'db> {
|
|||
if !self.typevar(db).is_same_typevar_as(db, other.typevar(db)) {
|
||||
return false;
|
||||
}
|
||||
other.lower(db).is_subtype_of(db, self.lower(db))
|
||||
&& self.upper(db).is_subtype_of(db, other.upper(db))
|
||||
other.lower(db).is_assignable_to(db, self.lower(db))
|
||||
&& self.upper(db).is_assignable_to(db, other.upper(db))
|
||||
}
|
||||
|
||||
/// Returns the intersection of two range constraints, or `None` if the intersection is empty.
|
||||
|
|
@ -724,7 +707,7 @@ impl<'db> ConstrainedTypeVar<'db> {
|
|||
|
||||
// If `lower ≰ upper`, then the intersection is empty, since there is no type that is both
|
||||
// greater than `lower`, and less than `upper`.
|
||||
if !lower.is_subtype_of(db, upper) {
|
||||
if !lower.is_assignable_to(db, upper) {
|
||||
return IntersectionResult::Disjoint;
|
||||
}
|
||||
|
||||
|
|
@ -1268,7 +1251,7 @@ impl<'db> Node<'db> {
|
|||
// process.
|
||||
debug_assert!(current_bounds.is_none_or(
|
||||
|(greatest_lower_bound, least_upper_bound)| {
|
||||
greatest_lower_bound.is_subtype_of(db, least_upper_bound)
|
||||
greatest_lower_bound.is_assignable_to(db, least_upper_bound)
|
||||
}
|
||||
));
|
||||
|
||||
|
|
@ -3397,8 +3380,8 @@ impl<'db> GenericContext<'db> {
|
|||
// do with that, so instead we just report the ambiguity as a specialization failure.
|
||||
let mut satisfied = false;
|
||||
let mut unconstrained = false;
|
||||
let mut greatest_lower_bound = Type::Never;
|
||||
let mut least_upper_bound = Type::object();
|
||||
let mut greatest_lower_bound = UnionBuilder::new(db).order_elements(true);
|
||||
let mut least_upper_bound = IntersectionBuilder::new(db).order_elements(true);
|
||||
let identity = bound_typevar.identity(db);
|
||||
tracing::trace!(
|
||||
target: "ty_python_semantic::types::constraints::specialize_constrained",
|
||||
|
|
@ -3417,10 +3400,8 @@ impl<'db> GenericContext<'db> {
|
|||
upper_bound = %upper_bound.display(db),
|
||||
"found representative type",
|
||||
);
|
||||
greatest_lower_bound =
|
||||
UnionType::from_elements(db, [greatest_lower_bound, lower_bound]);
|
||||
least_upper_bound =
|
||||
IntersectionType::from_elements(db, [least_upper_bound, upper_bound]);
|
||||
greatest_lower_bound.add_in_place(lower_bound);
|
||||
least_upper_bound.add_positive_in_place(upper_bound);
|
||||
}
|
||||
None => {
|
||||
unconstrained = true;
|
||||
|
|
@ -3454,7 +3435,9 @@ impl<'db> GenericContext<'db> {
|
|||
|
||||
// If `lower ≰ upper`, then there is no type that satisfies all of the paths in the
|
||||
// BDD. That's an ambiguous specialization, as described above.
|
||||
if !greatest_lower_bound.is_subtype_of(db, least_upper_bound) {
|
||||
let greatest_lower_bound = greatest_lower_bound.build();
|
||||
let least_upper_bound = least_upper_bound.build();
|
||||
if !greatest_lower_bound.is_assignable_to(db, least_upper_bound) {
|
||||
tracing::debug!(
|
||||
target: "ty_python_semantic::types::constraints::specialize_constrained",
|
||||
bound_typevar = %identity.display(db),
|
||||
|
|
|
|||
Loading…
Reference in New Issue