diff --git a/crates/red_knot_python_semantic/resources/mdtest/call/builtins.md b/crates/red_knot_python_semantic/resources/mdtest/call/builtins.md index 464358faa3..a7b9935ee6 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/call/builtins.md +++ b/crates/red_knot_python_semantic/resources/mdtest/call/builtins.md @@ -26,7 +26,11 @@ reveal_type(type(1)) # revealed: Literal[int] But a three-argument call to type creates a dynamic instance of the `type` class: ```py +class Base: ... + reveal_type(type("Foo", (), {})) # revealed: type + +reveal_type(type("Foo", (Base,), {"attr": 1})) # revealed: type ``` Other numbers of arguments are invalid @@ -39,6 +43,24 @@ type("Foo", ()) type("Foo", (), {}, weird_other_arg=42) ``` +The following calls are also invalid, due to incorrect argument types: + +```py +class Base: ... + +# error: [no-matching-overload] "No overload of class `type` matches arguments" +type(b"Foo", (), {}) + +# error: [no-matching-overload] "No overload of class `type` matches arguments" +type("Foo", Base, {}) + +# TODO: this should be an error +type("Foo", (1, 2), {}) + +# TODO: this should be an error +type("Foo", (Base,), {b"attr": 1}) +``` + ## Calls to `str()` ### Valid calls diff --git a/crates/red_knot_python_semantic/resources/mdtest/narrow/type.md b/crates/red_knot_python_semantic/resources/mdtest/narrow/type.md index 1e3d5f2f11..0b7f0f49b6 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/narrow/type.md +++ b/crates/red_knot_python_semantic/resources/mdtest/narrow/type.md @@ -76,6 +76,9 @@ No narrowing should occur if `type` is used to dynamically create a class: ```py def _(x: str | int): + # The following diagnostic is valid, since the three-argument form of `type` + # can only be called with `str` as the first argument. + # error: [no-matching-overload] "No overload of class `type` matches arguments" if type(x, (), {}) is str: reveal_type(x) # revealed: str | int else: diff --git a/crates/red_knot_python_semantic/resources/mdtest/type_properties/is_assignable_to.md b/crates/red_knot_python_semantic/resources/mdtest/type_properties/is_assignable_to.md index 6f9cf78028..bfc76cbdbd 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/type_properties/is_assignable_to.md +++ b/crates/red_knot_python_semantic/resources/mdtest/type_properties/is_assignable_to.md @@ -194,6 +194,9 @@ static_assert(is_assignable_to(tuple[int, str], tuple[int, str])) static_assert(is_assignable_to(tuple[Literal[1], Literal[2]], tuple[int, int])) static_assert(is_assignable_to(tuple[Any, Literal[2]], tuple[int, int])) static_assert(is_assignable_to(tuple[Literal[1], Any], tuple[int, int])) +static_assert(is_assignable_to(tuple[()], tuple)) +static_assert(is_assignable_to(tuple[int, str], tuple)) +static_assert(is_assignable_to(tuple[Any], tuple)) static_assert(not is_assignable_to(tuple[()], tuple[int])) static_assert(not is_assignable_to(tuple[int], tuple[str])) diff --git a/crates/red_knot_python_semantic/src/types.rs b/crates/red_knot_python_semantic/src/types.rs index beb7463eac..d15802b946 100644 --- a/crates/red_knot_python_semantic/src/types.rs +++ b/crates/red_knot_python_semantic/src/types.rs @@ -950,6 +950,13 @@ impl<'db> Type<'db> { ) } + // This special case is required because the left-hand side tuple might be a + // gradual type, so we can not rely on subtyping. This allows us to assign e.g. + // `tuple[Any, int]` to `tuple`. + (Type::Tuple(_), _) => KnownClass::Tuple + .to_instance(db) + .is_assignable_to(db, target), + // `type[Any]` is assignable to any `type[...]` type, because `type[Any]` can // materialize to any `type[...]` type. (Type::SubclassOf(subclass_of_ty), Type::SubclassOf(_)) @@ -2903,12 +2910,14 @@ impl<'db> Type<'db> { ), Signature::new( Parameters::new([ - Parameter::positional_only(Some(Name::new_static("o"))) - .with_annotated_type(Type::any()), + Parameter::positional_only(Some(Name::new_static("name"))) + .with_annotated_type(KnownClass::Str.to_instance(db)), Parameter::positional_only(Some(Name::new_static("bases"))) - .with_annotated_type(Type::any()), + // TODO: Should be tuple[type, ...] once we have support for homogenous tuples + .with_annotated_type(KnownClass::Tuple.to_instance(db)), Parameter::positional_only(Some(Name::new_static("dict"))) - .with_annotated_type(Type::any()), + // TODO: Should be `dict[str, Any]` once we have support for generics + .with_annotated_type(KnownClass::Dict.to_instance(db)), ]), Some(KnownClass::Type.to_instance(db)), ),