mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-01 22:31:47 +00:00
[ty] Protocols: Fixpoint iteration for fully-static check (#17880)
## Summary A recursive protocol like the following would previously lead to stack overflows when attempting to create the union type for the `P | None` member, because `UnionBuilder` checks if element types are fully static, and the fully-static check on `P` would in turn list all members and check whether all of them were fully static, leading to a cycle. ```py from __future__ import annotations from typing import Protocol class P(Protocol): parent: P | None ``` Here, we make the fully-static check on protocols a salsa query and add fixpoint iteration, starting with `true` as the initial value (assume that the recursive protocol is fully-static). If the recursive protocol has any non-fully-static members, we still return `false` when re-executing the query (see newly added tests). closes #17861 ## Test Plan Added regression test
This commit is contained in:
parent
a33d0d4bf4
commit
04457f99b6
5 changed files with 113 additions and 48 deletions
|
@ -1558,7 +1558,43 @@ def two(some_list: list, some_tuple: tuple[int, str], some_sized: Sized):
|
|||
c: Sized = some_sized
|
||||
```
|
||||
|
||||
## Regression test: narrowing with self-referential protocols
|
||||
## Recursive protocols
|
||||
|
||||
### Properties
|
||||
|
||||
```py
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Protocol, Any
|
||||
from ty_extensions import is_fully_static, static_assert, is_assignable_to, is_subtype_of, is_equivalent_to
|
||||
|
||||
class RecursiveFullyStatic(Protocol):
|
||||
parent: RecursiveFullyStatic | None
|
||||
x: int
|
||||
|
||||
class RecursiveNonFullyStatic(Protocol):
|
||||
parent: RecursiveNonFullyStatic | None
|
||||
x: Any
|
||||
|
||||
static_assert(is_fully_static(RecursiveFullyStatic))
|
||||
static_assert(not is_fully_static(RecursiveNonFullyStatic))
|
||||
|
||||
static_assert(not is_subtype_of(RecursiveFullyStatic, RecursiveNonFullyStatic))
|
||||
static_assert(not is_subtype_of(RecursiveNonFullyStatic, RecursiveFullyStatic))
|
||||
|
||||
# TODO: currently leads to a stack overflow
|
||||
# static_assert(is_assignable_to(RecursiveFullyStatic, RecursiveNonFullyStatic))
|
||||
# static_assert(is_assignable_to(RecursiveNonFullyStatic, RecursiveFullyStatic))
|
||||
|
||||
class AlsoRecursiveFullyStatic(Protocol):
|
||||
parent: AlsoRecursiveFullyStatic | None
|
||||
x: int
|
||||
|
||||
# TODO: currently leads to a stack overflow
|
||||
# static_assert(is_equivalent_to(AlsoRecursiveFullyStatic, RecursiveFullyStatic))
|
||||
```
|
||||
|
||||
### Regression test: narrowing with self-referential protocols
|
||||
|
||||
This snippet caused us to panic on an early version of the implementation for protocols.
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue