Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ A type is fully static iff it does not contain any gradual forms.
## Fully-static

```py
from typing_extensions import Literal, LiteralString, Never
from typing_extensions import Literal, LiteralString, Never, Callable
from knot_extensions import Intersection, Not, TypeOf, is_fully_static, static_assert

static_assert(is_fully_static(Never))
Expand Down Expand Up @@ -38,7 +38,7 @@ static_assert(is_fully_static(type[object]))
## Non-fully-static

```py
from typing_extensions import Any, Literal, LiteralString
from typing_extensions import Any, Literal, LiteralString, Callable
from knot_extensions import Intersection, Not, TypeOf, Unknown, is_fully_static, static_assert

static_assert(not is_fully_static(Any))
Expand All @@ -52,3 +52,26 @@ static_assert(not is_fully_static(tuple[Any, ...]))
static_assert(not is_fully_static(tuple[int, Any]))
static_assert(not is_fully_static(type[Any]))
```

## Callable

```py
from typing_extensions import Callable, Any
from knot_extensions import Unknown, is_fully_static, static_assert

static_assert(is_fully_static(Callable[[], int]))
static_assert(is_fully_static(Callable[[int, str], int]))

static_assert(not is_fully_static(Callable[..., int]))
static_assert(not is_fully_static(Callable[[], Any]))
static_assert(not is_fully_static(Callable[[int, Unknown], int]))
```

The invalid forms of `Callable` annotation are never fully static because we represent them with the
`(...) -> Unknown` signature.

```py
static_assert(not is_fully_static(Callable))
# error: [invalid-type-form]
static_assert(not is_fully_static(Callable[int, int]))
```
30 changes: 25 additions & 5 deletions crates/red_knot_python_semantic/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1331,11 +1331,7 @@ impl<'db> Type<'db> {
.elements(db)
.iter()
.all(|elem| elem.is_fully_static(db)),
Type::Callable(CallableType::General(_)) => {
// TODO: `Callable` is not fully static when the parameter argument is `...` or
// when any parameter type or return type is not fully static.
false
}
Type::Callable(CallableType::General(callable)) => callable.is_fully_static(db),
}
}

Expand Down Expand Up @@ -4518,6 +4514,30 @@ impl<'db> GeneralCallableType<'db> {
Signature::new(Parameters::unknown(), Some(Type::unknown())),
)
}

/// Returns `true` if this is a fully static callable type.
///
/// A callable type is fully static if all of its parameters and return type are fully static
/// and if it does not use gradual form (`...`) for its parameters.
pub(crate) fn is_fully_static(self, db: &'db dyn Db) -> bool {
let signature = self.signature(db);

if signature.parameters().is_gradual() {
return false;
}

if signature.parameters().iter().any(|parameter| {
parameter
.annotated_type()
.is_some_and(|annotated_type| !annotated_type.is_fully_static(db))
}) {
return false;
}

signature
.return_ty
.is_some_and(|return_type| return_type.is_fully_static(db))
}
}

/// A type that represents callable objects.
Expand Down
Loading