Skip to content

Commit 6de2b28

Browse files
authored
[red-knot] Check if callable type is fully static (#16633)
## Summary Part of #15382 This PR adds the check for whether a callable type is fully static or not. A callable type is fully static if all of the parameter types are fully static _and_ the return type is fully static _and_ if it does not use the gradual form (`...`) for its parameters. ## Test Plan Update `is_fully_static.md` with callable types. It seems that currently this test is grouped into either fully static or not, I think it would be useful to split them up in groups like callable, etc. I intentionally avoided that in this PR but I'll put up a PR for an appropriate split. Note: I've an explicit goal of updating the property tests with the new callable types once all relations are implemented.
1 parent 6b84253 commit 6de2b28

File tree

2 files changed

+50
-7
lines changed

2 files changed

+50
-7
lines changed

crates/red_knot_python_semantic/resources/mdtest/type_properties/is_fully_static.md

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ A type is fully static iff it does not contain any gradual forms.
55
## Fully-static
66

77
```py
8-
from typing_extensions import Literal, LiteralString, Never
8+
from typing_extensions import Literal, LiteralString, Never, Callable
99
from knot_extensions import Intersection, Not, TypeOf, is_fully_static, static_assert
1010

1111
static_assert(is_fully_static(Never))
@@ -38,7 +38,7 @@ static_assert(is_fully_static(type[object]))
3838
## Non-fully-static
3939

4040
```py
41-
from typing_extensions import Any, Literal, LiteralString
41+
from typing_extensions import Any, Literal, LiteralString, Callable
4242
from knot_extensions import Intersection, Not, TypeOf, Unknown, is_fully_static, static_assert
4343

4444
static_assert(not is_fully_static(Any))
@@ -52,3 +52,26 @@ static_assert(not is_fully_static(tuple[Any, ...]))
5252
static_assert(not is_fully_static(tuple[int, Any]))
5353
static_assert(not is_fully_static(type[Any]))
5454
```
55+
56+
## Callable
57+
58+
```py
59+
from typing_extensions import Callable, Any
60+
from knot_extensions import Unknown, is_fully_static, static_assert
61+
62+
static_assert(is_fully_static(Callable[[], int]))
63+
static_assert(is_fully_static(Callable[[int, str], int]))
64+
65+
static_assert(not is_fully_static(Callable[..., int]))
66+
static_assert(not is_fully_static(Callable[[], Any]))
67+
static_assert(not is_fully_static(Callable[[int, Unknown], int]))
68+
```
69+
70+
The invalid forms of `Callable` annotation are never fully static because we represent them with the
71+
`(...) -> Unknown` signature.
72+
73+
```py
74+
static_assert(not is_fully_static(Callable))
75+
# error: [invalid-type-form]
76+
static_assert(not is_fully_static(Callable[int, int]))
77+
```

crates/red_knot_python_semantic/src/types.rs

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1331,11 +1331,7 @@ impl<'db> Type<'db> {
13311331
.elements(db)
13321332
.iter()
13331333
.all(|elem| elem.is_fully_static(db)),
1334-
Type::Callable(CallableType::General(_)) => {
1335-
// TODO: `Callable` is not fully static when the parameter argument is `...` or
1336-
// when any parameter type or return type is not fully static.
1337-
false
1338-
}
1334+
Type::Callable(CallableType::General(callable)) => callable.is_fully_static(db),
13391335
}
13401336
}
13411337

@@ -4518,6 +4514,30 @@ impl<'db> GeneralCallableType<'db> {
45184514
Signature::new(Parameters::unknown(), Some(Type::unknown())),
45194515
)
45204516
}
4517+
4518+
/// Returns `true` if this is a fully static callable type.
4519+
///
4520+
/// A callable type is fully static if all of its parameters and return type are fully static
4521+
/// and if it does not use gradual form (`...`) for its parameters.
4522+
pub(crate) fn is_fully_static(self, db: &'db dyn Db) -> bool {
4523+
let signature = self.signature(db);
4524+
4525+
if signature.parameters().is_gradual() {
4526+
return false;
4527+
}
4528+
4529+
if signature.parameters().iter().any(|parameter| {
4530+
parameter
4531+
.annotated_type()
4532+
.is_some_and(|annotated_type| !annotated_type.is_fully_static(db))
4533+
}) {
4534+
return false;
4535+
}
4536+
4537+
signature
4538+
.return_ty
4539+
.is_some_and(|return_type| return_type.is_fully_static(db))
4540+
}
45214541
}
45224542

45234543
/// A type that represents callable objects.

0 commit comments

Comments
 (0)