diff --git a/crates/ty_python_semantic/resources/mdtest/type_properties/is_assignable_to.md b/crates/ty_python_semantic/resources/mdtest/type_properties/is_assignable_to.md index 93238ed137..7f800567db 100644 --- a/crates/ty_python_semantic/resources/mdtest/type_properties/is_assignable_to.md +++ b/crates/ty_python_semantic/resources/mdtest/type_properties/is_assignable_to.md @@ -125,6 +125,17 @@ static_assert(not is_assignable_to(Literal[b"foo"], Literal["foo"])) static_assert(not is_assignable_to(Literal["foo"], Literal[b"foo"])) ``` +### Slice literals + +The type of a slice literal is currently inferred as a specialization of `slice`. + +```py +from ty_extensions import TypeOf, is_assignable_to, static_assert + +static_assert(is_assignable_to(TypeOf[1:2:3], slice)) +static_assert(is_assignable_to(TypeOf[1:2:3], slice[int])) +``` + ## `type[…]` and class literals In the following tests, `TypeOf[str]` is a singleton type with a single inhabitant, the class `str`. diff --git a/crates/ty_python_semantic/resources/mdtest/type_properties/is_subtype_of.md b/crates/ty_python_semantic/resources/mdtest/type_properties/is_subtype_of.md index 4db6466b58..3132989bda 100644 --- a/crates/ty_python_semantic/resources/mdtest/type_properties/is_subtype_of.md +++ b/crates/ty_python_semantic/resources/mdtest/type_properties/is_subtype_of.md @@ -334,18 +334,13 @@ static_assert(is_subtype_of(TypeOf[typing], ModuleType)) ### Slice literals -The type of a slice literal is currently inferred as `slice`, which is a generic type whose default -specialization includes `Any`. Slice literals therefore do not participate in the subtyping -relationship. - -TODO: Infer a specialized type for the slice literal +The type of a slice literal is currently inferred as a specialization of `slice`. ```py from ty_extensions import TypeOf, is_subtype_of, static_assert +# slice's default specialization is slice[Any, Any, Any], which does not participate in subtyping. static_assert(not is_subtype_of(TypeOf[1:2:3], slice)) -# TODO: no error -# error: [static-assert-error] static_assert(is_subtype_of(TypeOf[1:2:3], slice[int])) ``` diff --git a/crates/ty_python_semantic/src/types.rs b/crates/ty_python_semantic/src/types.rs index 3d01d4e02c..f3956b2209 100644 --- a/crates/ty_python_semantic/src/types.rs +++ b/crates/ty_python_semantic/src/types.rs @@ -520,8 +520,6 @@ pub enum Type<'db> { LiteralString, /// A bytes literal BytesLiteral(BytesLiteralType<'db>), - /// A slice literal, e.g. `1:5`, `10:0:-1` or `:` - SliceLiteral(SliceLiteralType<'db>), /// A heterogeneous tuple type, with elements of the given types in source order. // TODO: Support variable length homogeneous tuple type like `tuple[int, ...]`. Tuple(TupleType<'db>), @@ -607,7 +605,6 @@ impl<'db> Type<'db> { | Self::StringLiteral(_) | Self::IntLiteral(_) | Self::LiteralString - | Self::SliceLiteral(_) | Self::Dynamic(DynamicType::Unknown | DynamicType::Any) | Self::BoundMethod(_) | Self::WrapperDescriptor(_) @@ -905,7 +902,6 @@ impl<'db> Type<'db> { | Type::AlwaysFalsy | Type::AlwaysTruthy | Type::BooleanLiteral(_) - | Type::SliceLiteral(_) | Type::BytesLiteral(_) | Type::StringLiteral(_) | Type::Dynamic(_) @@ -1102,15 +1098,13 @@ impl<'db> Type<'db> { | Type::BytesLiteral(_) | Type::ClassLiteral(_) | Type::FunctionLiteral(_) - | Type::ModuleLiteral(_) - | Type::SliceLiteral(_), + | Type::ModuleLiteral(_), Type::StringLiteral(_) | Type::IntLiteral(_) | Type::BytesLiteral(_) | Type::ClassLiteral(_) | Type::FunctionLiteral(_) - | Type::ModuleLiteral(_) - | Type::SliceLiteral(_), + | Type::ModuleLiteral(_), ) => false, // All `StringLiteral` types are a subtype of `LiteralString`. @@ -1132,9 +1126,6 @@ impl<'db> Type<'db> { (Type::ModuleLiteral(_), _) => KnownClass::ModuleType .to_instance(db) .is_subtype_of(db, target), - (Type::SliceLiteral(_), _) => { - KnownClass::Slice.to_instance(db).is_subtype_of(db, target) - } (Type::FunctionLiteral(self_function_literal), Type::Callable(_)) => { self_function_literal @@ -1516,10 +1507,6 @@ impl<'db> Type<'db> { false } - (Type::SliceLiteral(_), _) => KnownClass::Slice - .to_instance(db) - .is_assignable_to(db, target), - (Type::FunctionLiteral(self_function_literal), Type::Callable(_)) => { self_function_literal .into_callable_type(db) @@ -1711,7 +1698,6 @@ impl<'db> Type<'db> { | Type::IntLiteral(..) | Type::StringLiteral(..) | Type::BytesLiteral(..) - | Type::SliceLiteral(..) | Type::FunctionLiteral(..) | Type::BoundMethod(..) | Type::MethodWrapper(..) @@ -1724,7 +1710,6 @@ impl<'db> Type<'db> { | Type::IntLiteral(..) | Type::StringLiteral(..) | Type::BytesLiteral(..) - | Type::SliceLiteral(..) | Type::FunctionLiteral(..) | Type::BoundMethod(..) | Type::MethodWrapper(..) @@ -1751,7 +1736,6 @@ impl<'db> Type<'db> { | Type::DataclassDecorator(..) | Type::DataclassTransformer(..) | Type::IntLiteral(..) - | Type::SliceLiteral(..) | Type::StringLiteral(..) | Type::LiteralString, ) @@ -1768,7 +1752,6 @@ impl<'db> Type<'db> { | Type::DataclassDecorator(..) | Type::DataclassTransformer(..) | Type::IntLiteral(..) - | Type::SliceLiteral(..) | Type::StringLiteral(..) | Type::LiteralString, Type::Tuple(..), @@ -1799,7 +1782,6 @@ impl<'db> Type<'db> { | Type::StringLiteral(..) | Type::LiteralString | Type::BytesLiteral(..) - | Type::SliceLiteral(..) | Type::FunctionLiteral(..) | Type::BoundMethod(..) | Type::MethodWrapper(..) @@ -1812,7 +1794,6 @@ impl<'db> Type<'db> { | Type::StringLiteral(..) | Type::LiteralString | Type::BytesLiteral(..) - | Type::SliceLiteral(..) | Type::FunctionLiteral(..) | Type::BoundMethod(..) | Type::MethodWrapper(..) @@ -1848,7 +1829,6 @@ impl<'db> Type<'db> { | Type::StringLiteral(..) | Type::BytesLiteral(..) | Type::BooleanLiteral(..) - | Type::SliceLiteral(..) | Type::ClassLiteral(..) | Type::FunctionLiteral(..) | Type::ModuleLiteral(..) @@ -1862,7 +1842,6 @@ impl<'db> Type<'db> { | Type::StringLiteral(..) | Type::BytesLiteral(..) | Type::BooleanLiteral(..) - | Type::SliceLiteral(..) | Type::ClassLiteral(..) | Type::FunctionLiteral(..) | Type::ModuleLiteral(..) @@ -1953,13 +1932,6 @@ impl<'db> Type<'db> { !KnownClass::Bytes.is_subclass_of(db, instance.class()) } - (Type::SliceLiteral(..), Type::NominalInstance(instance)) - | (Type::NominalInstance(instance), Type::SliceLiteral(..)) => { - // A `Type::SliceLiteral` must be an instance of exactly `slice` - // (it cannot be an instance of a `slice` subclass) - !KnownClass::Slice.is_subclass_of(db, instance.class()) - } - // A class-literal type `X` is always disjoint from an instance type `Y`, // unless the type expressing "all instances of `Z`" is a subtype of of `Y`, // where `Z` is `X`'s metaclass. @@ -2005,14 +1977,8 @@ impl<'db> Type<'db> { false } - ( - Type::Callable(_), - Type::StringLiteral(_) | Type::BytesLiteral(_) | Type::SliceLiteral(_), - ) - | ( - Type::StringLiteral(_) | Type::BytesLiteral(_) | Type::SliceLiteral(_), - Type::Callable(_), - ) => { + (Type::Callable(_), Type::StringLiteral(_) | Type::BytesLiteral(_)) + | (Type::StringLiteral(_) | Type::BytesLiteral(_), Type::Callable(_)) => { // A callable type is disjoint from other literal types. For example, // `Type::StringLiteral` must be an instance of exactly `str`, not a subclass // of `str`, and `str` is not callable. The same applies to other literal types. @@ -2092,7 +2058,6 @@ impl<'db> Type<'db> { | Type::StringLiteral(_) | Type::LiteralString | Type::BytesLiteral(_) - | Type::SliceLiteral(_) | Type::KnownInstance(_) | Type::AlwaysFalsy | Type::AlwaysTruthy @@ -2157,7 +2122,6 @@ impl<'db> Type<'db> { | Type::IntLiteral(..) | Type::StringLiteral(..) | Type::BytesLiteral(..) - | Type::SliceLiteral(..) | Type::LiteralString => { // Note: The literal types included in this pattern are not true singletons. // There can be multiple Python objects (at different memory locations) that @@ -2284,7 +2248,6 @@ impl<'db> Type<'db> { | Type::BooleanLiteral(..) | Type::StringLiteral(..) | Type::BytesLiteral(..) - | Type::SliceLiteral(..) | Type::KnownInstance(..) => true, Type::ProtocolInstance(..) => { @@ -2481,7 +2444,6 @@ impl<'db> Type<'db> { | Type::StringLiteral(_) | Type::LiteralString | Type::BytesLiteral(_) - | Type::SliceLiteral(_) | Type::Tuple(_) | Type::TypeVar(_) | Type::NominalInstance(_) @@ -2591,7 +2553,6 @@ impl<'db> Type<'db> { KnownClass::Str.to_instance(db).instance_member(db, name) } Type::BytesLiteral(_) => KnownClass::Bytes.to_instance(db).instance_member(db, name), - Type::SliceLiteral(_) => KnownClass::Slice.to_instance(db).instance_member(db, name), Type::Tuple(_) => KnownClass::Tuple.to_instance(db).instance_member(db, name), Type::AlwaysTruthy | Type::AlwaysFalsy => Type::object(db).instance_member(db, name), @@ -3046,7 +3007,6 @@ impl<'db> Type<'db> { | Type::StringLiteral(..) | Type::BytesLiteral(..) | Type::LiteralString - | Type::SliceLiteral(..) | Type::Tuple(..) | Type::TypeVar(..) | Type::KnownInstance(..) @@ -3303,7 +3263,6 @@ impl<'db> Type<'db> { | Type::DataclassDecorator(_) | Type::DataclassTransformer(_) | Type::ModuleLiteral(_) - | Type::SliceLiteral(_) | Type::AlwaysTruthy => Truthiness::AlwaysTrue, Type::AlwaysFalsy => Truthiness::AlwaysFalse, @@ -4210,7 +4169,6 @@ impl<'db> Type<'db> { | Type::BytesLiteral(_) | Type::BooleanLiteral(_) | Type::LiteralString - | Type::SliceLiteral(_) | Type::Tuple(_) | Type::BoundSuper(_) | Type::TypeVar(_) @@ -4660,7 +4618,6 @@ impl<'db> Type<'db> { | Type::ModuleLiteral(_) | Type::IntLiteral(_) | Type::StringLiteral(_) - | Type::SliceLiteral(_) | Type::Tuple(_) | Type::TypeVar(_) | Type::LiteralString @@ -4713,7 +4670,6 @@ impl<'db> Type<'db> { | Type::BytesLiteral(_) | Type::AlwaysTruthy | Type::AlwaysFalsy - | Type::SliceLiteral(_) | Type::IntLiteral(_) | Type::LiteralString | Type::ModuleLiteral(_) @@ -4972,7 +4928,6 @@ impl<'db> Type<'db> { Type::Union(union) => union.map(db, |ty| ty.to_meta_type(db)), Type::BooleanLiteral(_) => KnownClass::Bool.to_class_literal(db), Type::BytesLiteral(_) => KnownClass::Bytes.to_class_literal(db), - Type::SliceLiteral(_) => KnownClass::Slice.to_class_literal(db), Type::IntLiteral(_) => KnownClass::Int.to_class_literal(db), Type::FunctionLiteral(_) => KnownClass::FunctionType.to_class_literal(db), Type::BoundMethod(_) => KnownClass::MethodType.to_class_literal(db), @@ -5151,7 +5106,6 @@ impl<'db> Type<'db> { | Type::LiteralString | Type::StringLiteral(_) | Type::BytesLiteral(_) - | Type::SliceLiteral(_) | Type::BoundSuper(_) // Same for `ProtocolInstance` | Type::ProtocolInstance(_) @@ -5242,7 +5196,6 @@ impl<'db> Type<'db> { | Type::LiteralString | Type::StringLiteral(_) | Type::BytesLiteral(_) - | Type::SliceLiteral(_) | Type::BoundSuper(_) | Type::NominalInstance(_) | Type::ProtocolInstance(_) @@ -5334,7 +5287,6 @@ impl<'db> Type<'db> { | Self::LiteralString | Self::IntLiteral(_) | Self::BytesLiteral(_) - | Self::SliceLiteral(_) | Self::MethodWrapper(_) | Self::WrapperDescriptor(_) | Self::DataclassDecorator(_) @@ -7947,18 +7899,6 @@ impl<'db> BytesLiteralType<'db> { } } -#[salsa::interned(debug)] -pub struct SliceLiteralType<'db> { - start: Option, - stop: Option, - step: Option, -} - -impl SliceLiteralType<'_> { - fn as_tuple(self, db: &dyn Db) -> (Option, Option, Option) { - (self.start(db), self.stop(db), self.step(db)) - } -} #[salsa::interned(debug)] pub struct TupleType<'db> { #[return_ref] diff --git a/crates/ty_python_semantic/src/types/class.rs b/crates/ty_python_semantic/src/types/class.rs index 783449ab76..e85c516e39 100644 --- a/crates/ty_python_semantic/src/types/class.rs +++ b/crates/ty_python_semantic/src/types/class.rs @@ -2662,6 +2662,47 @@ impl<'db> KnownClassLookupError<'db> { } } +pub(crate) struct SliceLiteral { + pub(crate) start: Option, + pub(crate) stop: Option, + pub(crate) step: Option, +} + +impl<'db> Type<'db> { + /// If this type represents a valid slice literal, returns a [`SliceLiteral`] describing it. + /// Otherwise returns `None`. + /// + /// The type must be a specialization of the `slice` builtin type, where the specialized + /// typevars are statically known integers or `None`. + pub(crate) fn slice_literal(self, db: &'db dyn Db) -> Option { + let ClassType::Generic(alias) = self.into_nominal_instance()?.class() else { + return None; + }; + if !alias.origin(db).is_known(db, KnownClass::Slice) { + return None; + } + let [start, stop, step] = alias.specialization(db).types(db).as_ref() else { + return None; + }; + + let to_u32 = |ty: &Type<'db>| match ty { + Type::IntLiteral(n) => i32::try_from(*n).map(Some).ok(), + Type::BooleanLiteral(b) => Some(Some(i32::from(*b))), + Type::NominalInstance(instance) + if instance.class().is_known(db, KnownClass::NoneType) => + { + Some(None) + } + _ => None, + }; + Some(SliceLiteral { + start: to_u32(start)?, + stop: to_u32(stop)?, + step: to_u32(step)?, + }) + } +} + #[derive(Debug, Clone, PartialEq, Eq, salsa::Update)] pub(super) struct MetaclassError<'db> { kind: MetaclassErrorKind<'db>, diff --git a/crates/ty_python_semantic/src/types/class_base.rs b/crates/ty_python_semantic/src/types/class_base.rs index cdf783e1d6..81458d1ea2 100644 --- a/crates/ty_python_semantic/src/types/class_base.rs +++ b/crates/ty_python_semantic/src/types/class_base.rs @@ -125,7 +125,6 @@ impl<'db> ClassBase<'db> { | Type::StringLiteral(_) | Type::LiteralString | Type::Tuple(_) - | Type::SliceLiteral(_) | Type::ModuleLiteral(_) | Type::SubclassOf(_) | Type::TypeVar(_) diff --git a/crates/ty_python_semantic/src/types/display.rs b/crates/ty_python_semantic/src/types/display.rs index 1da5a6eb61..8cbc90dec4 100644 --- a/crates/ty_python_semantic/src/types/display.rs +++ b/crates/ty_python_semantic/src/types/display.rs @@ -228,28 +228,6 @@ impl Display for DisplayRepresentation<'_> { escape.bytes_repr(TripleQuotes::No).write(f) } - Type::SliceLiteral(slice) => { - f.write_str("slice[")?; - if let Some(start) = slice.start(self.db) { - write!(f, "Literal[{start}]")?; - } else { - f.write_str("None")?; - } - - f.write_str(", ")?; - - if let Some(stop) = slice.stop(self.db) { - write!(f, "Literal[{stop}]")?; - } else { - f.write_str("None")?; - } - - if let Some(step) = slice.step(self.db) { - write!(f, ", Literal[{step}]")?; - } - - f.write_str("]") - } Type::Tuple(tuple) => { f.write_str("tuple[")?; let elements = tuple.elements(self.db); @@ -752,53 +730,9 @@ mod tests { use crate::db::tests::setup_db; use crate::symbol::typing_extensions_symbol; - use crate::types::{ - KnownClass, Parameter, Parameters, Signature, SliceLiteralType, StringLiteralType, Type, - }; + use crate::types::{KnownClass, Parameter, Parameters, Signature, StringLiteralType, Type}; use crate::Db; - #[test] - fn test_slice_literal_display() { - let db = setup_db(); - - assert_eq!( - Type::SliceLiteral(SliceLiteralType::new(&db, None, None, None)) - .display(&db) - .to_string(), - "slice[None, None]" - ); - assert_eq!( - Type::SliceLiteral(SliceLiteralType::new(&db, Some(1), None, None)) - .display(&db) - .to_string(), - "slice[Literal[1], None]" - ); - assert_eq!( - Type::SliceLiteral(SliceLiteralType::new(&db, None, Some(2), None)) - .display(&db) - .to_string(), - "slice[None, Literal[2]]" - ); - assert_eq!( - Type::SliceLiteral(SliceLiteralType::new(&db, Some(1), Some(5), None)) - .display(&db) - .to_string(), - "slice[Literal[1], Literal[5]]" - ); - assert_eq!( - Type::SliceLiteral(SliceLiteralType::new(&db, Some(1), Some(5), Some(2))) - .display(&db) - .to_string(), - "slice[Literal[1], Literal[5], Literal[2]]" - ); - assert_eq!( - Type::SliceLiteral(SliceLiteralType::new(&db, None, None, Some(2))) - .display(&db) - .to_string(), - "slice[None, None, Literal[2]]" - ); - } - #[test] fn string_literal_display() { let db = setup_db(); diff --git a/crates/ty_python_semantic/src/types/infer.rs b/crates/ty_python_semantic/src/types/infer.rs index 6ec79bf1a7..51d5d51ced 100644 --- a/crates/ty_python_semantic/src/types/infer.rs +++ b/crates/ty_python_semantic/src/types/infer.rs @@ -65,7 +65,7 @@ use crate::symbol::{ typing_extensions_symbol, Boundness, LookupError, }; use crate::types::call::{Argument, Bindings, CallArgumentTypes, CallArguments, CallError}; -use crate::types::class::MetaclassErrorKind; +use crate::types::class::{MetaclassErrorKind, SliceLiteral}; use crate::types::diagnostic::{ report_implicit_return_type, report_invalid_arguments_to_annotated, report_invalid_arguments_to_callable, report_invalid_assignment, @@ -87,10 +87,10 @@ use crate::types::{ ClassType, DataclassParams, DynamicType, FunctionDecorators, FunctionType, GenericAlias, IntersectionBuilder, IntersectionType, KnownClass, KnownFunction, KnownInstanceType, MemberLookupPolicy, MetaclassCandidate, Parameter, ParameterForm, Parameters, Signature, - Signatures, SliceLiteralType, StringLiteralType, SubclassOfType, Symbol, SymbolAndQualifiers, - Truthiness, TupleType, Type, TypeAliasType, TypeAndQualifiers, TypeArrayDisplay, - TypeQualifiers, TypeVarBoundOrConstraints, TypeVarInstance, TypeVarKind, TypeVarVariance, - UnionBuilder, UnionType, + Signatures, StringLiteralType, SubclassOfType, Symbol, SymbolAndQualifiers, Truthiness, + TupleType, Type, TypeAliasType, TypeAndQualifiers, TypeArrayDisplay, TypeQualifiers, + TypeVarBoundOrConstraints, TypeVarInstance, TypeVarKind, TypeVarVariance, UnionBuilder, + UnionType, }; use crate::unpack::{Unpack, UnpackPosition}; use crate::util::subscript::{PyIndex, PySlice}; @@ -2911,7 +2911,6 @@ impl<'db> TypeInferenceBuilder<'db> { | Type::StringLiteral(..) | Type::BytesLiteral(..) | Type::LiteralString - | Type::SliceLiteral(..) | Type::Tuple(..) | Type::KnownInstance(..) | Type::PropertyInstance(..) @@ -5653,7 +5652,6 @@ impl<'db> TypeInferenceBuilder<'db> { | Type::StringLiteral(_) | Type::LiteralString | Type::BytesLiteral(_) - | Type::SliceLiteral(_) | Type::Tuple(_) | Type::BoundSuper(_) | Type::TypeVar(_), @@ -5952,7 +5950,6 @@ impl<'db> TypeInferenceBuilder<'db> { | Type::StringLiteral(_) | Type::LiteralString | Type::BytesLiteral(_) - | Type::SliceLiteral(_) | Type::Tuple(_) | Type::BoundSuper(_) | Type::TypeVar(_), @@ -5978,7 +5975,6 @@ impl<'db> TypeInferenceBuilder<'db> { | Type::StringLiteral(_) | Type::LiteralString | Type::BytesLiteral(_) - | Type::SliceLiteral(_) | Type::Tuple(_) | Type::BoundSuper(_) | Type::TypeVar(_), @@ -6931,13 +6927,11 @@ impl<'db> TypeInferenceBuilder<'db> { value_ty: Type<'db>, slice_ty: Type<'db>, ) -> Type<'db> { - match (value_ty, slice_ty) { - ( - Type::NominalInstance(instance), - Type::IntLiteral(_) | Type::BooleanLiteral(_) | Type::SliceLiteral(_), - ) if instance - .class() - .is_known(self.db(), KnownClass::VersionInfo) => + match (value_ty, slice_ty, slice_ty.slice_literal(self.db())) { + (Type::NominalInstance(instance), _, _) + if instance + .class() + .is_known(self.db(), KnownClass::VersionInfo) => { self.infer_subscript_expression_types( value_node, @@ -6947,7 +6941,7 @@ impl<'db> TypeInferenceBuilder<'db> { } // Ex) Given `("a", "b", "c", "d")[1]`, return `"b"` - (Type::Tuple(tuple_ty), Type::IntLiteral(int)) if i32::try_from(int).is_ok() => { + (Type::Tuple(tuple_ty), Type::IntLiteral(int), _) if i32::try_from(int).is_ok() => { let elements = tuple_ty.elements(self.db()); elements .iter() @@ -6966,9 +6960,8 @@ impl<'db> TypeInferenceBuilder<'db> { }) } // Ex) Given `("a", 1, Null)[0:2]`, return `("a", 1)` - (Type::Tuple(tuple_ty), Type::SliceLiteral(slice_ty)) => { + (Type::Tuple(tuple_ty), _, Some(SliceLiteral { start, stop, step })) => { let elements = tuple_ty.elements(self.db()); - let (start, stop, step) = slice_ty.as_tuple(self.db()); if let Ok(new_elements) = elements.py_slice(start, stop, step) { TupleType::from_elements(self.db(), new_elements) @@ -6978,7 +6971,7 @@ impl<'db> TypeInferenceBuilder<'db> { } } // Ex) Given `"value"[1]`, return `"a"` - (Type::StringLiteral(literal_ty), Type::IntLiteral(int)) + (Type::StringLiteral(literal_ty), Type::IntLiteral(int), _) if i32::try_from(int).is_ok() => { let literal_value = literal_ty.value(self.db()); @@ -6999,9 +6992,8 @@ impl<'db> TypeInferenceBuilder<'db> { }) } // Ex) Given `"value"[1:3]`, return `"al"` - (Type::StringLiteral(literal_ty), Type::SliceLiteral(slice_ty)) => { + (Type::StringLiteral(literal_ty), _, Some(SliceLiteral { start, stop, step })) => { let literal_value = literal_ty.value(self.db()); - let (start, stop, step) = slice_ty.as_tuple(self.db()); let chars: Vec<_> = literal_value.chars().collect(); let result = if let Ok(new_chars) = chars.py_slice(start, stop, step) { @@ -7014,7 +7006,7 @@ impl<'db> TypeInferenceBuilder<'db> { result } // Ex) Given `b"value"[1]`, return `b"a"` - (Type::BytesLiteral(literal_ty), Type::IntLiteral(int)) + (Type::BytesLiteral(literal_ty), Type::IntLiteral(int), _) if i32::try_from(int).is_ok() => { let literal_value = literal_ty.value(self.db()); @@ -7035,9 +7027,8 @@ impl<'db> TypeInferenceBuilder<'db> { }) } // Ex) Given `b"value"[1:3]`, return `b"al"` - (Type::BytesLiteral(literal_ty), Type::SliceLiteral(slice_ty)) => { + (Type::BytesLiteral(literal_ty), _, Some(SliceLiteral { start, stop, step })) => { let literal_value = literal_ty.value(self.db()); - let (start, stop, step) = slice_ty.as_tuple(self.db()); if let Ok(new_bytes) = literal_value.py_slice(start, stop, step) { let new_bytes: Vec = new_bytes.copied().collect(); @@ -7051,29 +7042,30 @@ impl<'db> TypeInferenceBuilder<'db> { ( Type::Tuple(_) | Type::StringLiteral(_) | Type::BytesLiteral(_), Type::BooleanLiteral(bool), + _, ) => self.infer_subscript_expression_types( value_node, value_ty, Type::IntLiteral(i64::from(bool)), ), - (Type::KnownInstance(KnownInstanceType::Protocol), _) => { + (Type::KnownInstance(KnownInstanceType::Protocol), _, _) => { Type::Dynamic(DynamicType::SubscriptedProtocol) } - (Type::KnownInstance(KnownInstanceType::Generic(None)), Type::Tuple(typevars)) => { + (Type::KnownInstance(KnownInstanceType::Generic(None)), Type::Tuple(typevars), _) => { self.infer_subscript_legacy_generic_class(value_node, typevars.elements(self.db())) } - (Type::KnownInstance(KnownInstanceType::Generic(None)), typevar) => self + (Type::KnownInstance(KnownInstanceType::Generic(None)), typevar, _) => self .infer_subscript_legacy_generic_class(value_node, std::slice::from_ref(&typevar)), - (Type::KnownInstance(KnownInstanceType::Generic(Some(_))), _) => { + (Type::KnownInstance(KnownInstanceType::Generic(Some(_))), _, _) => { // TODO: emit a diagnostic todo_type!("doubly-specialized typing.Generic") } - (Type::KnownInstance(known_instance), _) + (Type::KnownInstance(known_instance), _, _) if known_instance.class().is_special_form() => { todo_type!("Inference of subscript on special form") } - (value_ty, slice_ty) => { + (value_ty, slice_ty, _) => { // If the class defines `__getitem__`, return its return type. // // See: https://docs.python.org/3/reference/datamodel.html#class-getitem-versus-getitem @@ -7252,8 +7244,8 @@ impl<'db> TypeInferenceBuilder<'db> { } fn infer_slice_expression(&mut self, slice: &ast::ExprSlice) -> Type<'db> { - enum SliceArg { - Arg(Option), + enum SliceArg<'db> { + Arg(Type<'db>), Unsupported, } @@ -7269,17 +7261,13 @@ impl<'db> TypeInferenceBuilder<'db> { let ty_step = self.infer_optional_expression(step.as_deref()); let type_to_slice_argument = |ty: Option>| match ty { - Some(Type::IntLiteral(n)) => match i32::try_from(n) { - Ok(n) => SliceArg::Arg(Some(n)), - Err(_) => SliceArg::Unsupported, - }, - Some(Type::BooleanLiteral(b)) => SliceArg::Arg(Some(i32::from(b))), - Some(Type::NominalInstance(instance)) + Some(ty @ (Type::IntLiteral(_) | Type::BooleanLiteral(_))) => SliceArg::Arg(ty), + Some(ty @ Type::NominalInstance(instance)) if instance.class().is_known(self.db(), KnownClass::NoneType) => { - SliceArg::Arg(None) + SliceArg::Arg(ty) } - None => SliceArg::Arg(None), + None => SliceArg::Arg(Type::none(self.db())), _ => SliceArg::Unsupported, }; @@ -7289,7 +7277,7 @@ impl<'db> TypeInferenceBuilder<'db> { type_to_slice_argument(ty_step), ) { (SliceArg::Arg(lower), SliceArg::Arg(upper), SliceArg::Arg(step)) => { - Type::SliceLiteral(SliceLiteralType::new(self.db(), lower, upper, step)) + KnownClass::Slice.to_specialized_instance(self.db(), [lower, upper, step]) } _ => KnownClass::Slice.to_instance(self.db()), } diff --git a/crates/ty_python_semantic/src/types/type_ordering.rs b/crates/ty_python_semantic/src/types/type_ordering.rs index bd390aff6e..c577669970 100644 --- a/crates/ty_python_semantic/src/types/type_ordering.rs +++ b/crates/ty_python_semantic/src/types/type_ordering.rs @@ -64,10 +64,6 @@ pub(super) fn union_or_intersection_elements_ordering<'db>( (Type::BytesLiteral(_), _) => Ordering::Less, (_, Type::BytesLiteral(_)) => Ordering::Greater, - (Type::SliceLiteral(left), Type::SliceLiteral(right)) => left.cmp(right), - (Type::SliceLiteral(_), _) => Ordering::Less, - (_, Type::SliceLiteral(_)) => Ordering::Greater, - (Type::FunctionLiteral(left), Type::FunctionLiteral(right)) => left.cmp(right), (Type::FunctionLiteral(_), _) => Ordering::Less, (_, Type::FunctionLiteral(_)) => Ordering::Greater,