From e45dd5f053adca0f90d1463f553f0d2326fd689c Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Sat, 25 Jan 2025 18:05:12 +0000 Subject: [PATCH] [red-knot] Ensure differently ordered unions are considered equivalent when they appear inside tuples inside top-level intersections --- .../type_properties/is_equivalent_to.md | 12 +++++++++ crates/red_knot_python_semantic/src/types.rs | 26 ++++++++++++++++--- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/crates/red_knot_python_semantic/resources/mdtest/type_properties/is_equivalent_to.md b/crates/red_knot_python_semantic/resources/mdtest/type_properties/is_equivalent_to.md index 44fa07756c2bc..047a45fc2fd0b 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/type_properties/is_equivalent_to.md +++ b/crates/red_knot_python_semantic/resources/mdtest/type_properties/is_equivalent_to.md @@ -106,4 +106,16 @@ static_assert( ) ``` +## Intersections containing tuples containing unions + +```py +from knot_extensions import is_equivalent_to, static_assert, Intersection + +class P: ... +class Q: ... +class R: ... + +static_assert(is_equivalent_to(Intersection[tuple[P | Q], R], Intersection[tuple[Q | P], R])) +``` + [the equivalence relation]: https://typing.readthedocs.io/en/latest/spec/glossary.html#term-equivalent diff --git a/crates/red_knot_python_semantic/src/types.rs b/crates/red_knot_python_semantic/src/types.rs index 14dfb013946c0..a056c176c7cd0 100644 --- a/crates/red_knot_python_semantic/src/types.rs +++ b/crates/red_knot_python_semantic/src/types.rs @@ -4516,21 +4516,33 @@ impl<'db> IntersectionType<'db> { } let self_positive = self.positive(db); + if !all_fully_static(db, self_positive) { return false; } - let self_negative = self.negative(db); - if !all_fully_static(db, self_negative) { + let other_positive = other.positive(db); + + if self_positive.len() != other_positive.len() { return false; } - let other_positive = other.positive(db); if !all_fully_static(db, other_positive) { return false; } + let self_negative = self.negative(db); + + if !all_fully_static(db, self_negative) { + return false; + } + let other_negative = other.negative(db); + + if self_negative.len() != other_negative.len() { + return false; + } + if !all_fully_static(db, other_negative) { return false; } @@ -4539,7 +4551,13 @@ impl<'db> IntersectionType<'db> { return true; } - self_positive.set_eq(other_positive) && self_negative.set_eq(other_negative) + let sorted_self = self.to_sorted_intersection(db); + + if sorted_self == other { + return true; + } + + sorted_self == other.to_sorted_intersection(db) } /// Return `true` if `self` has exactly the same set of possible static materializations as `other`