Skip to content

Commit 7b0d801

Browse files
committed
[red-knot] Support overloads for callable equivalence
1 parent 504fa20 commit 7b0d801

File tree

2 files changed

+74
-4
lines changed

2 files changed

+74
-4
lines changed

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

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,65 @@ static_assert(is_equivalent_to(int | Callable[[int | str], None], Callable[[str
256256

257257
### Overloads
258258

259-
TODO
259+
#### One overload
260+
261+
`overloaded.pyi`:
262+
263+
```pyi
264+
from typing import overload
265+
266+
class Grandparent: ...
267+
class Parent(Grandparent): ...
268+
class Child(Parent): ...
269+
270+
@overload
271+
def overloaded(a: Child) -> None: ...
272+
@overload
273+
def overloaded(a: Parent) -> None: ...
274+
@overload
275+
def overloaded(a: Grandparent) -> None: ...
276+
```
277+
278+
```py
279+
from knot_extensions import CallableTypeOf, is_equivalent_to, static_assert
280+
from overloaded import Grandparent, Parent, Child, overloaded
281+
282+
def grandparent(a: Grandparent) -> None: ...
283+
284+
static_assert(is_equivalent_to(CallableTypeOf[grandparent], CallableTypeOf[overloaded]))
285+
static_assert(is_equivalent_to(CallableTypeOf[overloaded], CallableTypeOf[grandparent]))
286+
```
287+
288+
#### Both overloads
289+
290+
`overloaded.pyi`:
291+
292+
```pyi
293+
from typing import overload
294+
295+
class Grandparent: ...
296+
class Parent(Grandparent): ...
297+
class Child(Parent): ...
298+
299+
@overload
300+
def pg(a: Parent) -> None: ...
301+
@overload
302+
def pg(a: Grandparent) -> None: ...
303+
304+
@overload
305+
def cpg(a: Child) -> None: ...
306+
@overload
307+
def cpg(a: Parent) -> None: ...
308+
@overload
309+
def cpg(a: Grandparent) -> None: ...
310+
```
311+
312+
```py
313+
from knot_extensions import CallableTypeOf, is_equivalent_to, static_assert
314+
from overloaded import pg, cpg
315+
316+
static_assert(is_equivalent_to(CallableTypeOf[pg], CallableTypeOf[cpg]))
317+
static_assert(is_equivalent_to(CallableTypeOf[cpg], CallableTypeOf[pg]))
318+
```
260319

261320
[the equivalence relation]: https://typing.python.org/en/latest/spec/glossary.html#term-equivalent

crates/red_knot_python_semantic/src/types.rs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6777,11 +6777,22 @@ impl<'db> CallableType<'db> {
67776777
fn is_equivalent_to(self, db: &'db dyn Db, other: Self) -> bool {
67786778
match (&**self.signatures(db), &**other.signatures(db)) {
67796779
([self_signature], [other_signature]) => {
6780+
// Common case: both callable types contain a single signature, use the custom
6781+
// equivalence check instead of delegating it to the subtype check.
67806782
self_signature.is_equivalent_to(db, other_signature)
67816783
}
6782-
_ => {
6783-
// TODO: overloads
6784-
false
6784+
(self_signatures, other_signatures) => {
6785+
if !self_signatures
6786+
.iter()
6787+
.chain(other_signatures.iter())
6788+
.all(|signature| signature.is_fully_static(db))
6789+
{
6790+
return false;
6791+
}
6792+
if self == other {
6793+
return true;
6794+
}
6795+
self.is_subtype_of(db, other) && other.is_subtype_of(db, self)
67856796
}
67866797
}
67876798
}

0 commit comments

Comments
 (0)