Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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()))

Expand Down Expand Up @@ -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]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, this seems like the right solution here

reveal_type(generic_context(bounded).specialize_constrained(ConstraintSet.range(Never, T, Any)))
# revealed: None
reveal_type(generic_context(bounded).specialize_constrained(ConstraintSet.never()))

Expand All @@ -94,26 +100,42 @@ 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)))

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
Expand Down Expand Up @@ -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)))
Comment on lines 166 to +169
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are None because we have no basis for picking between Base and Unrelated, so we'll just fall back to Unknown?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's right

# 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)))
Expand Down Expand Up @@ -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]
Expand All @@ -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()))

Expand Down Expand Up @@ -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]]
Expand All @@ -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]]
Expand All @@ -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]]
Expand Down
28 changes: 25 additions & 3 deletions crates/ty_python_semantic/src/types/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -668,28 +668,41 @@ 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,
}

impl<'db> IntersectionBuilder<'db> {
pub(crate) fn new(db: &'db dyn Db) -> Self {
Self {
db,
order_elements: false,
intersections: vec![InnerIntersectionBuilder::default()],
}
}

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>,
Expand Down Expand Up @@ -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)),
)
}
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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))
}
}
Expand Down
Loading
Loading