diff --git a/crates/ty_python_semantic/resources/corpus/cyclic_implicit_attr_union.py b/crates/ty_python_semantic/resources/corpus/cyclic_implicit_attr_union.py new file mode 100644 index 0000000000..ffce93c61f --- /dev/null +++ b/crates/ty_python_semantic/resources/corpus/cyclic_implicit_attr_union.py @@ -0,0 +1,10 @@ +# regression test for https://github.com/astral-sh/ty/issues/2085 + +class Foo: + def __init__(self, x: int): + self.left = x + self.right = x + def method(self): + self.left, self.right = self.right, self.left + if self.right: + self.right = self.right diff --git a/crates/ty_python_semantic/src/types.rs b/crates/ty_python_semantic/src/types.rs index bcca8e77fe..cc05c9c000 100644 --- a/crates/ty_python_semantic/src/types.rs +++ b/crates/ty_python_semantic/src/types.rs @@ -965,7 +965,10 @@ impl<'db> Type<'db> { if has_divergent_type_in_cycle(previous) && !has_divergent_type_in_cycle(self) { self } else { - UnionType::from_elements_cycle_recovery(db, [self, previous]) + // The current type is unioned to the previous type. Unioning in the reverse order can cause the fixed-point iterations to converge slowly or even fail. + // Consider the case where the order of union types is different between the previous and current cycle. + // We should use the previous union type as the base and only add new element types in this cycle, if any. + UnionType::from_elements_cycle_recovery(db, [previous, self]) } } }