[ty] Remove SliceLiteral type variant (#17958)

@AlexWaygood pointed out that the `SliceLiteral` type variant was
originally created to handle slices before we had generics.
https://github.com/astral-sh/ruff/pull/17927#discussion_r2078115787

Now that we _do_ have generics, we can use a specialization of the
`slice` builtin type for slice literals.

This depends on https://github.com/astral-sh/ruff/pull/17956, since we
need to make sure that all typevar defaults are fully substituted when
specializing `slice`.
This commit is contained in:
Douglas Creager 2025-05-08 20:16:41 -04:00 committed by GitHub
parent b705664d49
commit f78367979e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 89 additions and 185 deletions

View file

@ -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<i32>,
stop: Option<i32>,
step: Option<i32>,
}
impl SliceLiteralType<'_> {
fn as_tuple(self, db: &dyn Db) -> (Option<i32>, Option<i32>, Option<i32>) {
(self.start(db), self.stop(db), self.step(db))
}
}
#[salsa::interned(debug)]
pub struct TupleType<'db> {
#[return_ref]

View file

@ -2662,6 +2662,47 @@ impl<'db> KnownClassLookupError<'db> {
}
}
pub(crate) struct SliceLiteral {
pub(crate) start: Option<i32>,
pub(crate) stop: Option<i32>,
pub(crate) step: Option<i32>,
}
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<SliceLiteral> {
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>,

View file

@ -125,7 +125,6 @@ impl<'db> ClassBase<'db> {
| Type::StringLiteral(_)
| Type::LiteralString
| Type::Tuple(_)
| Type::SliceLiteral(_)
| Type::ModuleLiteral(_)
| Type::SubclassOf(_)
| Type::TypeVar(_)

View file

@ -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();

View file

@ -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<u8> = 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<i32>),
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<Type<'db>>| 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()),
}

View file

@ -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,