[red-knot] add cycle handling for FunctionType::signature query

move some ecosystem projects from bad to good

add mdtest
This commit is contained in:
Carl Meyer 2025-05-04 07:54:43 -07:00
parent e95130ad80
commit 3bf77a9635
No known key found for this signature in database
GPG key ID: 2D1FB7916A52E121
5 changed files with 53 additions and 8 deletions

View file

@ -0,0 +1,15 @@
# Cycles
## Function signature
Deferred annotations can result in cycles in resolving a function signature:
```py
from __future__ import annotations
# error: [invalid-type-form]
def f(x: f):
pass
reveal_type(f) # revealed: def f(x: Unknown) -> Unknown
```

View file

@ -1,4 +1,3 @@
Expression # cycle panic (signature_)
Tanjun # cycle panic (signature_)
altair # cycle panics (try_metaclass_)
antidote # hangs / slow
@ -12,21 +11,16 @@ hydpy # cycle panics (try_metaclass_)
ibis # cycle panics (try_metaclass_)
manticore # stack overflow
materialize # stack overflow
mypy # cycle panic (signature_)
pandas # slow
pandas-stubs # cycle panics (try_metaclass_)
pandera # cycle panics (try_metaclass_)
prefect # slow
pylint # cycle panics (self-recursive type alias)
pytest # cycle panics (signature_)
pywin32 # bad use-def map (binding with definitely-visible unbound)
schemathesis # cycle panics (signature_)
scikit-learn # success, but mypy-primer hangs processing the output
scipy # missing expression type ("expression should belong to this TypeInference region")
spack # success, but mypy-primer hangs processing the output
spark # cycle panics (try_metaclass_)
steam.py # cycle panics (try_metaclass_), often hangs when multi-threaded
streamlit # cycle panic (signature_)
sympy # stack overflow
trio # cycle panics (deferred annotatation resolving in wrong scope)
xarray # cycle panics (try_metaclass_)

View file

@ -1,4 +1,5 @@
AutoSplit
Expression
PyGithub
PyWinCtl
SinbadCogs
@ -55,6 +56,7 @@ mkdocs
mkosi
mongo-python-driver
more-itertools
mypy
mypy-protobuf
mypy_primer
nionutils
@ -82,6 +84,7 @@ pylox
pyodide
pyp
pyppeteer
pytest
pytest-robotframework
python-chess
python-htmlgen
@ -90,6 +93,7 @@ rclip
rich
rotki
schema_salad
schemathesis
scrapy
setuptools
sockeye
@ -99,7 +103,9 @@ starlette
static-frame
stone
strawberry
streamlit
tornado
trio
twine
typeshed-stats
urllib3

View file

@ -6495,6 +6495,11 @@ impl<'db> FunctionSignature<'db> {
pub(crate) fn iter(&self) -> Iter<Signature<'db>> {
self.as_slice().iter()
}
/// Returns the "bottom" signature (subtype of all fully-static signatures.)
pub(crate) fn bottom(db: &'db dyn Db) -> Self {
Self::Single(Signature::bottom(db))
}
}
impl<'db> IntoIterator for &'db FunctionSignature<'db> {
@ -6639,7 +6644,7 @@ impl<'db> FunctionType<'db> {
///
/// Were this not a salsa query, then the calling query
/// would depend on the function's AST and rerun for every change in that file.
#[salsa::tracked(return_ref)]
#[salsa::tracked(return_ref, cycle_fn=signature_cycle_recover, cycle_initial=signature_cycle_initial)]
pub(crate) fn signature(self, db: &'db dyn Db) -> FunctionSignature<'db> {
if let Some(overloaded) = self.to_overloaded(db) {
FunctionSignature::Overloaded(
@ -6849,6 +6854,22 @@ impl<'db> FunctionType<'db> {
}
}
fn signature_cycle_recover<'db>(
_db: &'db dyn Db,
_value: &FunctionSignature<'db>,
_count: u32,
_function: FunctionType<'db>,
) -> salsa::CycleRecoveryAction<FunctionSignature<'db>> {
salsa::CycleRecoveryAction::Iterate
}
fn signature_cycle_initial<'db>(
db: &'db dyn Db,
_function: FunctionType<'db>,
) -> FunctionSignature<'db> {
FunctionSignature::bottom(db)
}
/// Non-exhaustive enumeration of known functions (e.g. `builtins.reveal_type`, ...) that might
/// have special behavior.
#[derive(

View file

@ -291,6 +291,16 @@ impl<'db> Signature<'db> {
}
}
/// Return the "bottom" signature, subtype of all other fully-static signatures.
pub(crate) fn bottom(db: &'db dyn Db) -> Self {
Self {
generic_context: None,
inherited_generic_context: None,
parameters: Parameters::object(db),
return_ty: Some(Type::Never),
}
}
pub(crate) fn normalized(&self, db: &'db dyn Db) -> Self {
Self {
generic_context: self.generic_context,
@ -957,7 +967,6 @@ impl<'db> Parameters<'db> {
}
/// Return parameters that represents `(*args: object, **kwargs: object)`.
#[cfg(test)]
pub(crate) fn object(db: &'db dyn Db) -> Self {
Self {
value: vec![