Skip to content

Commit

Permalink
fix pre-commit & micro-optimise
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexWaygood committed Jan 17, 2025
1 parent 1e47693 commit 9bf4a77
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,21 +42,21 @@ class Q: ...
class R: ...
class S: ...

static_assert(is_equivalent_to(P | Q | R, P | R | Q)) #1
static_assert(is_equivalent_to(P | Q | R, Q | P | R)) #2
static_assert(is_equivalent_to(P | Q | R, Q | R | P)) #3
static_assert(is_equivalent_to(P | Q | R, R | P | Q)) #4
static_assert(is_equivalent_to(P | Q | R, R | Q | P)) #5
static_assert(is_equivalent_to(P | R | Q, Q | P | R)) #6
static_assert(is_equivalent_to(P | R | Q, Q | R | P)) #7
static_assert(is_equivalent_to(P | R | Q, R | P | Q)) #8
static_assert(is_equivalent_to(P | R | Q, R | Q | P)) #9
static_assert(is_equivalent_to(Q | P | R, Q | R | P)) #10
static_assert(is_equivalent_to(Q | P | R, R | P | Q)) #11
static_assert(is_equivalent_to(Q | P | R, R | Q | P)) #12
static_assert(is_equivalent_to(Q | R | P, R | P | Q)) #13
static_assert(is_equivalent_to(Q | R | P, R | Q | P)) #14
static_assert(is_equivalent_to(R | P | Q, R | Q | P)) #15
static_assert(is_equivalent_to(P | Q | R, P | R | Q)) # 1
static_assert(is_equivalent_to(P | Q | R, Q | P | R)) # 2
static_assert(is_equivalent_to(P | Q | R, Q | R | P)) # 3
static_assert(is_equivalent_to(P | Q | R, R | P | Q)) # 4
static_assert(is_equivalent_to(P | Q | R, R | Q | P)) # 5
static_assert(is_equivalent_to(P | R | Q, Q | P | R)) # 6
static_assert(is_equivalent_to(P | R | Q, Q | R | P)) # 7
static_assert(is_equivalent_to(P | R | Q, R | P | Q)) # 8
static_assert(is_equivalent_to(P | R | Q, R | Q | P)) # 9
static_assert(is_equivalent_to(Q | P | R, Q | R | P)) # 10
static_assert(is_equivalent_to(Q | P | R, R | P | Q)) # 11
static_assert(is_equivalent_to(Q | P | R, R | Q | P)) # 12
static_assert(is_equivalent_to(Q | R | P, R | P | Q)) # 13
static_assert(is_equivalent_to(Q | R | P, R | Q | P)) # 14
static_assert(is_equivalent_to(R | P | Q, R | Q | P)) # 15

static_assert(is_equivalent_to(str | None, None | str))

Expand Down
45 changes: 24 additions & 21 deletions crates/red_knot_python_semantic/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4215,37 +4215,33 @@ impl<'db> UnionType<'db> {
Self::from_elements(db, self.elements(db).iter().map(transform_fn))
}

/// Return `true` if the elements in this union type are already sorted according to the algorithm
/// given by the [`union_elements_ordering`] routine, *and* all intersection elements in the union
/// are also internally sorted.
fn is_sorted(self, db: &'db dyn Db) -> bool {
/// Return a new [`UnionType`] instance with the same elements as `self`,
/// but sorted according to a canonical ordering.
fn to_sorted_union(self, db: &'db dyn Db) -> UnionType<'db> {
let elements = self.elements(db);

type_ordering::sequence_is_sorted(elements)
// short-circuit if the elements are already sorted
if type_ordering::sequence_is_sorted(elements)
&& elements
.iter()
.rev() // optimisation: intersections come last in the ordering
.copied()
.skip_while(|ty| !ty.is_intersection())
.map_while(Type::into_intersection)
.all(|intersection| intersection.is_sorted(db))
}

/// Return a new [`UnionType`] instance with the same elements as `self`,
/// but sorted according to a canonical ordering.
fn to_sorted_union(self, db: &'db dyn Db) -> UnionType<'db> {
if self.is_sorted(db) {
{
return self;
}

let mut elements = self.elements_boxed(db).to_vec();
for element in &mut elements {
let mut new_elements = elements.to_vec();
for element in &mut new_elements {
if let Type::Intersection(intersection) = element {
*element = Type::Intersection(intersection.to_sorted_intersection(db));
}
}
elements.sort_unstable_by(union_elements_ordering);
new_elements.sort_unstable_by(union_elements_ordering);

UnionType::new(db, elements.into_boxed_slice())
UnionType::new(db, new_elements.into_boxed_slice())
}
}

Expand Down Expand Up @@ -4275,17 +4271,24 @@ impl<'db> IntersectionType<'db> {
/// Return a new [`IntersectionType`] instance with the same elements as `self`,
/// but sorted according to a canonical ordering.
fn to_sorted_intersection(self, db: &'db dyn Db) -> IntersectionType<'db> {
if self.is_sorted(db) {
let positive = self.positive(db);
let negative = self.negative(db);

// Inline `IntersectionType::is_sorted` here so we don't have to lookup `self.positive`
// and `self.negative` twice in the same function
if type_ordering::sequence_is_sorted(positive)
&& type_ordering::sequence_is_sorted(negative)
{
return self;
}

let mut positive = self.positive(db).clone();
positive.sort_unstable_by(union_elements_ordering);
let mut new_positive = positive.clone();
new_positive.sort_unstable_by(union_elements_ordering);

let mut negative = self.negative(db).clone();
negative.sort_unstable_by(union_elements_ordering);
let mut new_negative = negative.clone();
new_negative.sort_unstable_by(union_elements_ordering);

IntersectionType::new(db, positive, negative)
IntersectionType::new(db, new_positive, new_negative)
}
}

Expand Down
5 changes: 2 additions & 3 deletions crates/red_knot_python_semantic/src/types/type_ordering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@ use std::cmp::Ordering;

use itertools::Itertools;

use crate::types::Type;

use super::{
class_base::ClassBase, ClassLiteralType, DynamicType, InstanceType, KnownInstanceType, TodoType,
class_base::ClassBase, ClassLiteralType, DynamicType, InstanceType, KnownInstanceType,
TodoType, Type,
};

/// Return an [`Ordering`] that describes the canonical order in which two types should appear
Expand Down

0 comments on commit 9bf4a77

Please sign in to comment.