mirror of https://github.com/astral-sh/ruff
[ty] avoid fixpoint unioning of types containing current-cycle Divergent (#21910)
Partially addresses https://github.com/astral-sh/ty/issues/1732 ## Summary Don't union the previous type in fixpoint iteration if the previous type contains a `Divergent` from the current cycle and the latest type does not. The theory here, as outlined by @mtshiba at https://github.com/astral-sh/ty/issues/1732#issuecomment-3609937420, is that oscillation can't occur by removing and then reintroducing a `Divergent` type repeatedly, since `Divergent` types are only introduced at the start of fixpoint iteration. ## Test Plan Removes a `Divergent` type from the added mdtest, doesn't otherwise regress any tests.
This commit is contained in:
parent
5e42926eee
commit
0138cd238a
|
|
@ -141,3 +141,18 @@ class C:
|
||||||
# revealed: (*, kw_only=Unknown | ((*, kw_only=Unknown) -> Unknown)) -> Unknown
|
# revealed: (*, kw_only=Unknown | ((*, kw_only=Unknown) -> Unknown)) -> Unknown
|
||||||
reveal_type(self.d)
|
reveal_type(self.d)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Self-referential implicit attributes
|
||||||
|
|
||||||
|
```py
|
||||||
|
class Cyclic:
|
||||||
|
def __init__(self, data: str | dict):
|
||||||
|
self.data = data
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
if isinstance(self.data, str):
|
||||||
|
self.data = {"url": self.data}
|
||||||
|
|
||||||
|
# revealed: Unknown | str | dict[Unknown, Unknown] | dict[Unknown | str, Unknown | str]
|
||||||
|
reveal_type(Cyclic("").data)
|
||||||
|
```
|
||||||
|
|
|
||||||
|
|
@ -922,7 +922,30 @@ impl<'db> Type<'db> {
|
||||||
{
|
{
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
_ => UnionType::from_elements_cycle_recovery(db, [self, previous]),
|
_ => {
|
||||||
|
// Also avoid unioning in a previous type which contains a Divergent from the
|
||||||
|
// current cycle, if the most-recent type does not. This cannot cause an
|
||||||
|
// oscillation, since Divergent is only introduced at the start of fixpoint
|
||||||
|
// iteration.
|
||||||
|
let has_divergent_type_in_cycle = |ty| {
|
||||||
|
any_over_type(
|
||||||
|
db,
|
||||||
|
ty,
|
||||||
|
&|nested_ty| {
|
||||||
|
matches!(
|
||||||
|
nested_ty,
|
||||||
|
Type::Dynamic(DynamicType::Divergent(DivergentType { id }))
|
||||||
|
if cycle.head_ids().contains(&id))
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if has_divergent_type_in_cycle(previous) && !has_divergent_type_in_cycle(self) {
|
||||||
|
self
|
||||||
|
} else {
|
||||||
|
UnionType::from_elements_cycle_recovery(db, [self, previous])
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.recursive_type_normalized(db, cycle)
|
.recursive_type_normalized(db, cycle)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue