mirror of
https://github.com/astral-sh/ruff
synced 2026-01-21 13:30:49 -05:00
[ty] stabilize union-type ordering in fixed-point iteration (#22070)
## Summary This PR fixes https://github.com/astral-sh/ty/issues/2085. Based on the reported code, the panicking MRE is: ```python class Test: 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 ``` The type inference (`implicit_attribute_inner`) for `self.right` proceeds as follows: ``` 0: Divergent(Id(6c07)) 1: Unknown | int | (Divergent(Id(1c00)) & ~AlwaysFalsy) 2: Unknown | int | (Divergent(Id(6c07)) & ~AlwaysFalsy) | (Divergent(Id(1c00)) & ~AlwaysFalsy) 3: Unknown | int | (Divergent(Id(1c00)) & ~AlwaysFalsy) | (Divergent(Id(6c07)) & ~AlwaysFalsy) 4: Unknown | int | (Divergent(Id(6c07)) & ~AlwaysFalsy) | (Divergent(Id(1c00)) & ~AlwaysFalsy) ... ``` The problem is that the order of union types is not stable between cycles. To solve this, when unioning the previous union type with the current union type, we should use the previous type as the base and add only the new elements in this cycle (In the current implementation, this unioning order was reversed). ## Test Plan New corpus test
This commit is contained in:
committed by
GitHub
parent
664686bdbc
commit
06db474f20
@@ -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
|
||||
@@ -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])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user