Skip to content

Commit 3838203

Browse files
committed
fix latent bug regarding synthesized __new__ methods for class literals
1 parent 7a24e6d commit 3838203

File tree

2 files changed

+43
-30
lines changed

2 files changed

+43
-30
lines changed

crates/ty_python_semantic/resources/mdtest/type_properties/is_assignable_to.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -912,6 +912,7 @@ c: Callable[[Any], str] = A().g
912912

913913
```py
914914
from typing import Any, Callable
915+
from ty_extensions import static_assert, is_assignable_to
915916

916917
c: Callable[[object], type] = type
917918
c: Callable[[str], Any] = str
@@ -932,6 +933,15 @@ class C:
932933
def __init__(self, x: int) -> None: ...
933934

934935
c: Callable[[int], C] = C
936+
937+
def f(a: Callable[..., Any], b: Callable[[Any], Any]): ...
938+
939+
f(tuple, tuple)
940+
941+
def g(a: Callable[[Any, Any], Any]): ...
942+
943+
# error: [invalid-argument-type] "Argument to function `g` is incorrect: Expected `(Any, Any, /) -> Any`, found `<class 'tuple'>`"
944+
g(tuple)
935945
```
936946

937947
### Generic class literal types

crates/ty_python_semantic/src/types/class.rs

Lines changed: 33 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -727,38 +727,41 @@ impl<'db> ClassType<'db> {
727727
)
728728
.place;
729729

730-
let dunder_new_function =
731-
if let Place::Type(Type::FunctionLiteral(dunder_new_function), _) =
732-
dunder_new_function_symbol
733-
{
734-
// Step 3: If the return type of the `__new__` evaluates to a type that is not a subclass of this class,
735-
// then we should ignore the `__init__` and just return the `__new__` method.
736-
let returns_non_subclass =
737-
dunder_new_function
738-
.signature(db)
739-
.overloads
740-
.iter()
741-
.any(|signature| {
742-
signature.return_ty.is_some_and(|return_ty| {
743-
!return_ty.is_assignable_to(
744-
db,
745-
self_ty
746-
.to_instance(db)
747-
.expect("ClassType should be instantiable"),
748-
)
749-
})
750-
});
730+
let dunder_new_signature = dunder_new_function_symbol
731+
.ignore_possibly_unbound()
732+
.and_then(|ty| match ty {
733+
Type::FunctionLiteral(function) => Some(function.signature(db)),
734+
Type::Callable(callable) => Some(callable.signatures(db)),
735+
_ => None,
736+
});
751737

752-
let dunder_new_bound_method =
753-
dunder_new_function.into_bound_method_type(db, self_ty);
738+
let dunder_new_function = if let Some(dunder_new_signature) = dunder_new_signature {
739+
// Step 3: If the return type of the `__new__` evaluates to a type that is not a subclass of this class,
740+
// then we should ignore the `__init__` and just return the `__new__` method.
741+
let returns_non_subclass = dunder_new_signature.overloads.iter().any(|signature| {
742+
signature.return_ty.is_some_and(|return_ty| {
743+
!return_ty.is_assignable_to(
744+
db,
745+
self_ty
746+
.to_instance(db)
747+
.expect("ClassType should be instantiable"),
748+
)
749+
})
750+
});
754751

755-
if returns_non_subclass {
756-
return dunder_new_bound_method;
757-
}
758-
Some(dunder_new_bound_method)
759-
} else {
760-
None
761-
};
752+
let dunder_new_bound_method = Type::Callable(CallableType::new(
753+
db,
754+
dunder_new_signature.bind_self(),
755+
true,
756+
));
757+
758+
if returns_non_subclass {
759+
return dunder_new_bound_method;
760+
}
761+
Some(dunder_new_bound_method)
762+
} else {
763+
None
764+
};
762765

763766
let dunder_init_function_symbol = self_ty
764767
.member_lookup_with_policy(

0 commit comments

Comments
 (0)