diff --git a/crates/ty_python_semantic/resources/mdtest/binary/tuples.md b/crates/ty_python_semantic/resources/mdtest/binary/tuples.md index ae23e4090a..78279831fd 100644 --- a/crates/ty_python_semantic/resources/mdtest/binary/tuples.md +++ b/crates/ty_python_semantic/resources/mdtest/binary/tuples.md @@ -17,7 +17,6 @@ def _(x: tuple[int, str], y: tuple[None, tuple[int]]): ```py def _(x: tuple[int, ...], y: tuple[str, ...]): - # TODO: should be `tuple[int | str, ...]` - reveal_type(x + y) # revealed: tuple[int | Unknown, ...] + reveal_type(x + y) # revealed: tuple[int | str, ...] reveal_type(x + (1, 2)) # revealed: tuple[int, ...] ``` diff --git a/crates/ty_python_semantic/resources/mdtest/generics/legacy/functions.md b/crates/ty_python_semantic/resources/mdtest/generics/legacy/functions.md index 62ffaf561e..d188f269a8 100644 --- a/crates/ty_python_semantic/resources/mdtest/generics/legacy/functions.md +++ b/crates/ty_python_semantic/resources/mdtest/generics/legacy/functions.md @@ -88,14 +88,12 @@ def takes_in_protocol(x: CanIndex[T]) -> T: return x[0] def deep_list(x: list[str]) -> None: - # TODO: revealed: list[str] - reveal_type(takes_in_list(x)) # revealed: list[Unknown] + reveal_type(takes_in_list(x)) # revealed: list[str] # TODO: revealed: str reveal_type(takes_in_protocol(x)) # revealed: Unknown def deeper_list(x: list[set[str]]) -> None: - # TODO: revealed: list[set[str]] - reveal_type(takes_in_list(x)) # revealed: list[Unknown] + reveal_type(takes_in_list(x)) # revealed: list[set[str]] # TODO: revealed: set[str] reveal_type(takes_in_protocol(x)) # revealed: Unknown @@ -119,13 +117,11 @@ This also works when passing in arguments that are subclasses of the parameter t class Sub(list[int]): ... class GenericSub(list[T]): ... -# TODO: revealed: list[int] -reveal_type(takes_in_list(Sub())) # revealed: list[Unknown] +reveal_type(takes_in_list(Sub())) # revealed: list[int] # TODO: revealed: int reveal_type(takes_in_protocol(Sub())) # revealed: Unknown -# TODO: revealed: list[str] -reveal_type(takes_in_list(GenericSub[str]())) # revealed: list[Unknown] +reveal_type(takes_in_list(GenericSub[str]())) # revealed: list[str] # TODO: revealed: str reveal_type(takes_in_protocol(GenericSub[str]())) # revealed: Unknown diff --git a/crates/ty_python_semantic/resources/mdtest/generics/pep695/functions.md b/crates/ty_python_semantic/resources/mdtest/generics/pep695/functions.md index 31f8569e5c..11701f17e7 100644 --- a/crates/ty_python_semantic/resources/mdtest/generics/pep695/functions.md +++ b/crates/ty_python_semantic/resources/mdtest/generics/pep695/functions.md @@ -83,14 +83,12 @@ def takes_in_protocol[T](x: CanIndex[T]) -> T: return x[0] def deep_list(x: list[str]) -> None: - # TODO: revealed: list[str] - reveal_type(takes_in_list(x)) # revealed: list[Unknown] + reveal_type(takes_in_list(x)) # revealed: list[str] # TODO: revealed: str reveal_type(takes_in_protocol(x)) # revealed: Unknown def deeper_list(x: list[set[str]]) -> None: - # TODO: revealed: list[set[str]] - reveal_type(takes_in_list(x)) # revealed: list[Unknown] + reveal_type(takes_in_list(x)) # revealed: list[set[str]] # TODO: revealed: set[str] reveal_type(takes_in_protocol(x)) # revealed: Unknown @@ -114,13 +112,11 @@ This also works when passing in arguments that are subclasses of the parameter t class Sub(list[int]): ... class GenericSub[T](list[T]): ... -# TODO: revealed: list[int] -reveal_type(takes_in_list(Sub())) # revealed: list[Unknown] +reveal_type(takes_in_list(Sub())) # revealed: list[int] # TODO: revealed: int reveal_type(takes_in_protocol(Sub())) # revealed: Unknown -# TODO: revealed: list[str] -reveal_type(takes_in_list(GenericSub[str]())) # revealed: list[Unknown] +reveal_type(takes_in_list(GenericSub[str]())) # revealed: list[str] # TODO: revealed: str reveal_type(takes_in_protocol(GenericSub[str]())) # revealed: Unknown diff --git a/crates/ty_python_semantic/src/types.rs b/crates/ty_python_semantic/src/types.rs index 60418aac62..9e536cbf31 100644 --- a/crates/ty_python_semantic/src/types.rs +++ b/crates/ty_python_semantic/src/types.rs @@ -562,25 +562,22 @@ impl<'db> Type<'db> { fn is_none(&self, db: &'db dyn Db) -> bool { self.into_nominal_instance() - .is_some_and(|instance| instance.class().is_known(db, KnownClass::NoneType)) + .is_some_and(|instance| instance.class.is_known(db, KnownClass::NoneType)) } fn is_bool(&self, db: &'db dyn Db) -> bool { self.into_nominal_instance() - .is_some_and(|instance| instance.class().is_known(db, KnownClass::Bool)) + .is_some_and(|instance| instance.class.is_known(db, KnownClass::Bool)) } pub fn is_notimplemented(&self, db: &'db dyn Db) -> bool { - self.into_nominal_instance().is_some_and(|instance| { - instance - .class() - .is_known(db, KnownClass::NotImplementedType) - }) + self.into_nominal_instance() + .is_some_and(|instance| instance.class.is_known(db, KnownClass::NotImplementedType)) } pub fn is_object(&self, db: &'db dyn Db) -> bool { self.into_nominal_instance() - .is_some_and(|instance| instance.class().is_object(db)) + .is_some_and(|instance| instance.class.is_object(db)) } pub const fn is_todo(&self) -> bool { @@ -1063,7 +1060,7 @@ impl<'db> Type<'db> { (_, Type::Never) => false, // Everything is a subtype of `object`. - (_, Type::NominalInstance(instance)) if instance.class().is_object(db) => true, + (_, Type::NominalInstance(instance)) if instance.class.is_object(db) => true, // In general, a TypeVar `T` is not a subtype of a type `S` unless one of the two conditions is satisfied: // 1. `T` is a bound TypeVar and `T`'s upper bound is a subtype of `S`. @@ -1373,7 +1370,7 @@ impl<'db> Type<'db> { // All types are assignable to `object`. // TODO this special case might be removable once the below cases are comprehensive - (_, Type::NominalInstance(instance)) if instance.class().is_object(db) => true, + (_, Type::NominalInstance(instance)) if instance.class.is_object(db) => true, // In general, a TypeVar `T` is not assignable to a type `S` unless one of the two conditions is satisfied: // 1. `T` is a bound TypeVar and `T`'s upper bound is assignable to `S`. @@ -1547,7 +1544,7 @@ impl<'db> Type<'db> { } (Type::NominalInstance(instance), Type::Callable(_)) - if instance.class().is_subclass_of_any_or_unknown(db) => + if instance.class.is_subclass_of_any_or_unknown(db) => { true } @@ -1616,7 +1613,7 @@ impl<'db> Type<'db> { } (Type::ProtocolInstance(protocol), nominal @ Type::NominalInstance(n)) | (nominal @ Type::NominalInstance(n), Type::ProtocolInstance(protocol)) => { - n.class().is_object(db) && protocol.normalized(db) == nominal + n.class.is_object(db) && protocol.normalized(db) == nominal } _ => self == other && self.is_fully_static(db) && other.is_fully_static(db), } @@ -1671,7 +1668,7 @@ impl<'db> Type<'db> { } (Type::ProtocolInstance(protocol), nominal @ Type::NominalInstance(n)) | (nominal @ Type::NominalInstance(n), Type::ProtocolInstance(protocol)) => { - n.class().is_object(db) && protocol.normalized(db) == nominal + n.class.is_object(db) && protocol.normalized(db) == nominal } _ => false, } @@ -1883,7 +1880,7 @@ impl<'db> Type<'db> { // member on `protocol`. (Type::ProtocolInstance(protocol), nominal @ Type::NominalInstance(n)) | (nominal @ Type::NominalInstance(n), Type::ProtocolInstance(protocol)) => { - n.class().is_final(db) && !nominal.satisfies_protocol(db, protocol) + n.class.is_final(db) && !nominal.satisfies_protocol(db, protocol) } ( @@ -1948,7 +1945,7 @@ impl<'db> Type<'db> { (Type::KnownInstance(known_instance), Type::NominalInstance(instance)) | (Type::NominalInstance(instance), Type::KnownInstance(known_instance)) => { - !known_instance.is_instance_of(db, instance.class()) + !known_instance.is_instance_of(db, instance.class) } (known_instance_ty @ Type::KnownInstance(_), Type::Tuple(tuple)) @@ -1960,7 +1957,7 @@ impl<'db> Type<'db> { | (Type::NominalInstance(instance), Type::BooleanLiteral(..)) => { // A `Type::BooleanLiteral()` must be an instance of exactly `bool` // (it cannot be an instance of a `bool` subclass) - !KnownClass::Bool.is_subclass_of(db, instance.class()) + !KnownClass::Bool.is_subclass_of(db, instance.class) } (Type::BooleanLiteral(..), _) | (_, Type::BooleanLiteral(..)) => true, @@ -1969,7 +1966,7 @@ impl<'db> Type<'db> { | (Type::NominalInstance(instance), Type::IntLiteral(..)) => { // A `Type::IntLiteral()` must be an instance of exactly `int` // (it cannot be an instance of an `int` subclass) - !KnownClass::Int.is_subclass_of(db, instance.class()) + !KnownClass::Int.is_subclass_of(db, instance.class) } (Type::IntLiteral(..), _) | (_, Type::IntLiteral(..)) => true, @@ -1981,7 +1978,7 @@ impl<'db> Type<'db> { | (Type::NominalInstance(instance), Type::StringLiteral(..) | Type::LiteralString) => { // A `Type::StringLiteral()` or a `Type::LiteralString` must be an instance of exactly `str` // (it cannot be an instance of a `str` subclass) - !KnownClass::Str.is_subclass_of(db, instance.class()) + !KnownClass::Str.is_subclass_of(db, instance.class) } (Type::LiteralString, Type::LiteralString) => false, @@ -1991,7 +1988,7 @@ impl<'db> Type<'db> { | (Type::NominalInstance(instance), Type::BytesLiteral(..)) => { // A `Type::BytesLiteral()` must be an instance of exactly `bytes` // (it cannot be an instance of a `bytes` subclass) - !KnownClass::Bytes.is_subclass_of(db, instance.class()) + !KnownClass::Bytes.is_subclass_of(db, instance.class) } // A class-literal type `X` is always disjoint from an instance type `Y`, @@ -2012,7 +2009,7 @@ impl<'db> Type<'db> { | (Type::NominalInstance(instance), Type::FunctionLiteral(..)) => { // A `Type::FunctionLiteral()` must be an instance of exactly `types.FunctionType` // (it cannot be an instance of a `types.FunctionType` subclass) - !KnownClass::FunctionType.is_subclass_of(db, instance.class()) + !KnownClass::FunctionType.is_subclass_of(db, instance.class) } (Type::BoundMethod(_), other) | (other, Type::BoundMethod(_)) => KnownClass::MethodType @@ -2440,7 +2437,7 @@ impl<'db> Type<'db> { // i.e. Type::NominalInstance(type). So looking up a name in the MRO of // `Type::NominalInstance(type)` is equivalent to looking up the name in the // MRO of the class `object`. - Type::NominalInstance(instance) if instance.class().is_known(db, KnownClass::Type) => { + Type::NominalInstance(instance) if instance.class.is_known(db, KnownClass::Type) => { KnownClass::Object .to_class_literal(db) .find_name_in_mro_with_policy(db, name, policy) @@ -2530,7 +2527,7 @@ impl<'db> Type<'db> { Type::Dynamic(_) | Type::Never => Symbol::bound(self).into(), - Type::NominalInstance(instance) => instance.class().instance_member(db, name), + Type::NominalInstance(instance) => instance.class.instance_member(db, name), Type::ProtocolInstance(protocol) => protocol.instance_member(db, name), @@ -2978,7 +2975,7 @@ impl<'db> Type<'db> { Type::NominalInstance(instance) if matches!(name.as_str(), "major" | "minor") - && instance.class().is_known(db, KnownClass::VersionInfo) => + && instance.class.is_known(db, KnownClass::VersionInfo) => { let python_version = Program::get(db).python_version(db); let segment = if name == "major" { @@ -3050,7 +3047,7 @@ impl<'db> Type<'db> { // resolve the attribute. if matches!( self.into_nominal_instance() - .and_then(|instance| instance.class().known(db)), + .and_then(|instance| instance.class.known(db)), Some(KnownClass::ModuleType | KnownClass::GenericAlias) ) { return Symbol::Unbound.into(); @@ -3309,7 +3306,7 @@ impl<'db> Type<'db> { } }, - Type::NominalInstance(instance) => match instance.class().known(db) { + Type::NominalInstance(instance) => match instance.class.known(db) { Some(known_class) => known_class.bool(), None => try_dunder_bool()?, }, @@ -4863,7 +4860,7 @@ impl<'db> Type<'db> { Type::Dynamic(_) => Ok(*self), - Type::NominalInstance(instance) => match instance.class().known(db) { + Type::NominalInstance(instance) => match instance.class.known(db) { Some(KnownClass::TypeVar) => Ok(todo_type!( "Support for `typing.TypeVar` instances in type expressions" )), @@ -5291,7 +5288,7 @@ impl<'db> Type<'db> { } Self::GenericAlias(alias) => Some(TypeDefinition::Class(alias.definition(db))), Self::NominalInstance(instance) => { - Some(TypeDefinition::Class(instance.class().definition(db))) + Some(TypeDefinition::Class(instance.class.definition(db))) } Self::KnownInstance(instance) => match instance { KnownInstanceType::TypeVar(var) => { @@ -8046,7 +8043,7 @@ impl<'db> SuperOwnerKind<'db> { Either::Left(ClassBase::Dynamic(dynamic).mro(db, None)) } SuperOwnerKind::Class(class) => Either::Right(class.iter_mro(db)), - SuperOwnerKind::Instance(instance) => Either::Right(instance.class().iter_mro(db)), + SuperOwnerKind::Instance(instance) => Either::Right(instance.class.iter_mro(db)), } } @@ -8062,7 +8059,7 @@ impl<'db> SuperOwnerKind<'db> { match self { SuperOwnerKind::Dynamic(_) => None, SuperOwnerKind::Class(class) => Some(class), - SuperOwnerKind::Instance(instance) => Some(instance.class()), + SuperOwnerKind::Instance(instance) => Some(instance.class), } } @@ -8240,7 +8237,7 @@ impl<'db> BoundSuperType<'db> { .expect("Calling `find_name_in_mro` on dynamic type should return `Some`") } SuperOwnerKind::Class(class) => class, - SuperOwnerKind::Instance(instance) => instance.class(), + SuperOwnerKind::Instance(instance) => instance.class, }; let (class_literal, _) = class.class_literal(db); diff --git a/crates/ty_python_semantic/src/types/builder.rs b/crates/ty_python_semantic/src/types/builder.rs index 01b5fa8c46..5b55088a8b 100644 --- a/crates/ty_python_semantic/src/types/builder.rs +++ b/crates/ty_python_semantic/src/types/builder.rs @@ -614,7 +614,7 @@ impl<'db> InnerIntersectionBuilder<'db> { _ => { let known_instance = new_positive .into_nominal_instance() - .and_then(|instance| instance.class().known(db)); + .and_then(|instance| instance.class.known(db)); if known_instance == Some(KnownClass::Object) { // `object & T` -> `T`; it is always redundant to add `object` to an intersection @@ -634,7 +634,7 @@ impl<'db> InnerIntersectionBuilder<'db> { new_positive = Type::BooleanLiteral(false); } Type::NominalInstance(instance) - if instance.class().is_known(db, KnownClass::Bool) => + if instance.class.is_known(db, KnownClass::Bool) => { match new_positive { // `bool & AlwaysTruthy` -> `Literal[True]` @@ -728,7 +728,7 @@ impl<'db> InnerIntersectionBuilder<'db> { self.positive .iter() .filter_map(|ty| ty.into_nominal_instance()) - .filter_map(|instance| instance.class().known(db)) + .filter_map(|instance| instance.class.known(db)) .any(KnownClass::is_bool) }; @@ -744,7 +744,7 @@ impl<'db> InnerIntersectionBuilder<'db> { Type::Never => { // Adding ~Never to an intersection is a no-op. } - Type::NominalInstance(instance) if instance.class().is_object(db) => { + Type::NominalInstance(instance) if instance.class.is_object(db) => { // Adding ~object to an intersection results in Never. *self = Self::default(); self.positive.insert(Type::Never); diff --git a/crates/ty_python_semantic/src/types/class.rs b/crates/ty_python_semantic/src/types/class.rs index 150915f9eb..96673012f1 100644 --- a/crates/ty_python_semantic/src/types/class.rs +++ b/crates/ty_python_semantic/src/types/class.rs @@ -2733,7 +2733,7 @@ impl<'db> Type<'db> { /// 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 { + let ClassType::Generic(alias) = self.into_nominal_instance()?.class else { return None; }; if !alias.origin(db).is_known(db, KnownClass::Slice) { @@ -2747,7 +2747,7 @@ impl<'db> Type<'db> { 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) => + if instance.class.is_known(db, KnownClass::NoneType) => { Some(None) } diff --git a/crates/ty_python_semantic/src/types/class_base.rs b/crates/ty_python_semantic/src/types/class_base.rs index d11b124571..bcd2af97cb 100644 --- a/crates/ty_python_semantic/src/types/class_base.rs +++ b/crates/ty_python_semantic/src/types/class_base.rs @@ -103,7 +103,7 @@ impl<'db> ClassBase<'db> { } Type::GenericAlias(generic) => Some(Self::Class(ClassType::Generic(generic))), Type::NominalInstance(instance) - if instance.class().is_known(db, KnownClass::GenericAlias) => + if instance.class.is_known(db, KnownClass::GenericAlias) => { Self::try_from_type(db, todo_type!("GenericAlias instance")) } diff --git a/crates/ty_python_semantic/src/types/display.rs b/crates/ty_python_semantic/src/types/display.rs index d0716a6459..467a577905 100644 --- a/crates/ty_python_semantic/src/types/display.rs +++ b/crates/ty_python_semantic/src/types/display.rs @@ -69,7 +69,7 @@ impl Display for DisplayRepresentation<'_> { Type::Dynamic(dynamic) => dynamic.fmt(f), Type::Never => f.write_str("Never"), Type::NominalInstance(instance) => { - match (instance.class(), instance.class().known(self.db)) { + match (instance.class, instance.class.known(self.db)) { (_, Some(KnownClass::NoneType)) => f.write_str("None"), (_, Some(KnownClass::NoDefaultType)) => f.write_str("NoDefault"), (ClassType::NonGeneric(class), _) => f.write_str(class.name(self.db)), diff --git a/crates/ty_python_semantic/src/types/generics.rs b/crates/ty_python_semantic/src/types/generics.rs index cacad480c8..b5f2b3ef83 100644 --- a/crates/ty_python_semantic/src/types/generics.rs +++ b/crates/ty_python_semantic/src/types/generics.rs @@ -2,6 +2,9 @@ use ruff_python_ast as ast; use rustc_hash::FxHashMap; use crate::semantic_index::SemanticIndex; +use crate::types::class::ClassType; +use crate::types::class_base::ClassBase; +use crate::types::instance::NominalInstanceType; use crate::types::signatures::{Parameter, Parameters, Signature}; use crate::types::{ declaration_type, todo_type, KnownInstanceType, Type, TypeVarBoundOrConstraints, @@ -671,6 +674,35 @@ impl<'db> SpecializationBuilder<'db> { } } + ( + Type::NominalInstance(NominalInstanceType { + class: ClassType::Generic(formal_alias), + .. + }), + Type::NominalInstance(NominalInstanceType { + class: actual_class, + .. + }), + ) => { + let formal_origin = formal_alias.origin(self.db); + for base in actual_class.iter_mro(self.db) { + let ClassBase::Class(ClassType::Generic(base_alias)) = base else { + continue; + }; + if formal_origin != base_alias.origin(self.db) { + continue; + } + let formal_specialization = formal_alias.specialization(self.db).types(self.db); + let base_specialization = base_alias.specialization(self.db).types(self.db); + for (formal_ty, base_ty) in + formal_specialization.iter().zip(base_specialization) + { + self.infer(*formal_ty, *base_ty)?; + } + return Ok(()); + } + } + (Type::Union(formal), _) => { // TODO: We haven't implemented a full unification solver yet. If typevars appear // in multiple union elements, we ideally want to express that _only one_ of them diff --git a/crates/ty_python_semantic/src/types/infer.rs b/crates/ty_python_semantic/src/types/infer.rs index 8f3878a01d..8ac4ec9ba0 100644 --- a/crates/ty_python_semantic/src/types/infer.rs +++ b/crates/ty_python_semantic/src/types/infer.rs @@ -1384,7 +1384,7 @@ impl<'db> TypeInferenceBuilder<'db> { Type::BooleanLiteral(_) | Type::IntLiteral(_) => {} Type::NominalInstance(instance) if matches!( - instance.class().known(self.db()), + instance.class.known(self.db()), Some(KnownClass::Float | KnownClass::Int | KnownClass::Bool) ) => {} _ => return false, @@ -2477,7 +2477,7 @@ impl<'db> TypeInferenceBuilder<'db> { definition: Definition<'db>, ) { fn extract_tuple_specialization<'db>(db: &'db dyn Db, ty: Type<'db>) -> Option> { - let class = ty.into_nominal_instance()?.class(); + let class = ty.into_nominal_instance()?.class; if !class.is_known(db, KnownClass::Tuple) { return None; } @@ -2933,7 +2933,7 @@ impl<'db> TypeInferenceBuilder<'db> { } // Super instances do not allow attribute assignment - Type::NominalInstance(instance) if instance.class().is_known(db, KnownClass::Super) => { + Type::NominalInstance(instance) if instance.class.is_known(db, KnownClass::Super) => { if emit_diagnostics { if let Some(builder) = self.context.report_lint(&INVALID_ASSIGNMENT, target) { builder.into_diagnostic(format_args!( @@ -3462,10 +3462,7 @@ impl<'db> TypeInferenceBuilder<'db> { // Handle various singletons. if let Type::NominalInstance(instance) = declared_ty.inner_type() { - if instance - .class() - .is_known(self.db(), KnownClass::SpecialForm) - { + if instance.class.is_known(self.db(), KnownClass::SpecialForm) { if let Some(name_expr) = target.as_name_expr() { if let Some(known_instance) = KnownInstanceType::try_from_file_and_name( self.db(), @@ -6593,9 +6590,7 @@ impl<'db> TypeInferenceBuilder<'db> { range, ), (Type::Tuple(_), Type::NominalInstance(instance)) - if instance - .class() - .is_known(self.db(), KnownClass::VersionInfo) => + if instance.class.is_known(self.db(), KnownClass::VersionInfo) => { self.infer_binary_type_comparison( left, @@ -6605,9 +6600,7 @@ impl<'db> TypeInferenceBuilder<'db> { ) } (Type::NominalInstance(instance), Type::Tuple(_)) - if instance - .class() - .is_known(self.db(), KnownClass::VersionInfo) => + if instance.class.is_known(self.db(), KnownClass::VersionInfo) => { self.infer_binary_type_comparison( Type::version_info_tuple(self.db()), @@ -6999,9 +6992,7 @@ impl<'db> TypeInferenceBuilder<'db> { ) -> Type<'db> { match (value_ty, slice_ty, slice_ty.slice_literal(self.db())) { (Type::NominalInstance(instance), _, _) - if instance - .class() - .is_known(self.db(), KnownClass::VersionInfo) => + if instance.class.is_known(self.db(), KnownClass::VersionInfo) => { self.infer_subscript_expression_types( value_node, @@ -7362,7 +7353,7 @@ impl<'db> TypeInferenceBuilder<'db> { let type_to_slice_argument = |ty: Option>| match ty { Some(ty @ (Type::IntLiteral(_) | Type::BooleanLiteral(_))) => SliceArg::Arg(ty), Some(ty @ Type::NominalInstance(instance)) - if instance.class().is_known(self.db(), KnownClass::NoneType) => + if instance.class.is_known(self.db(), KnownClass::NoneType) => { SliceArg::Arg(ty) } diff --git a/crates/ty_python_semantic/src/types/instance.rs b/crates/ty_python_semantic/src/types/instance.rs index a232400670..b33d430609 100644 --- a/crates/ty_python_semantic/src/types/instance.rs +++ b/crates/ty_python_semantic/src/types/instance.rs @@ -1,5 +1,7 @@ //! Instance types: both nominal and structural. +use std::marker::PhantomData; + use super::protocol_class::ProtocolInterface; use super::{ClassType, KnownClass, SubclassOfType, Type}; use crate::symbol::{Symbol, SymbolAndQualifiers}; @@ -14,7 +16,10 @@ impl<'db> Type<'db> { if class.class_literal(db).0.is_protocol(db) { Self::ProtocolInstance(ProtocolInstanceType(Protocol::FromClass(class))) } else { - Self::NominalInstance(NominalInstanceType { class }) + Self::NominalInstance(NominalInstanceType { + class, + _phantom: PhantomData, + }) } } @@ -56,16 +61,14 @@ impl<'db> Type<'db> { /// A type representing the set of runtime objects which are instances of a certain nominal class. #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, salsa::Update)] pub struct NominalInstanceType<'db> { + pub(super) class: ClassType<'db>, + // Keep this field private, so that the only way of constructing `NominalInstanceType` instances // is through the `Type::instance` constructor function. - class: ClassType<'db>, + _phantom: PhantomData<()>, } impl<'db> NominalInstanceType<'db> { - pub(super) fn class(self) -> ClassType<'db> { - self.class - } - pub(super) fn is_subtype_of(self, db: &'db dyn Db, other: Self) -> bool { // N.B. The subclass relation is fully static self.class.is_subclass_of(db, other.class) @@ -130,6 +133,7 @@ impl<'db> NominalInstanceType<'db> { ) -> Self { Self { class: self.class.apply_type_mapping(db, type_mapping), + _phantom: PhantomData, } } diff --git a/crates/ty_python_semantic/src/types/narrow.rs b/crates/ty_python_semantic/src/types/narrow.rs index 175e787cbf..473035fdf7 100644 --- a/crates/ty_python_semantic/src/types/narrow.rs +++ b/crates/ty_python_semantic/src/types/narrow.rs @@ -474,7 +474,7 @@ impl<'db> NarrowingConstraintsBuilder<'db> { } // Treat `bool` as `Literal[True, False]`. Type::NominalInstance(instance) - if instance.class().is_known(db, KnownClass::Bool) => + if instance.class.is_known(db, KnownClass::Bool) => { UnionType::from_elements( db, @@ -505,7 +505,7 @@ impl<'db> NarrowingConstraintsBuilder<'db> { fn evaluate_expr_ne(&mut self, lhs_ty: Type<'db>, rhs_ty: Type<'db>) -> Option> { match (lhs_ty, rhs_ty) { (Type::NominalInstance(instance), Type::IntLiteral(i)) - if instance.class().is_known(self.db, KnownClass::Bool) => + if instance.class.is_known(self.db, KnownClass::Bool) => { if i == 0 { Some(Type::BooleanLiteral(false).negate(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 b264aba1ad..52dd6ee28a 100644 --- a/crates/ty_python_semantic/src/types/type_ordering.rs +++ b/crates/ty_python_semantic/src/types/type_ordering.rs @@ -126,9 +126,7 @@ pub(super) fn union_or_intersection_elements_ordering<'db>( (Type::SubclassOf(_), _) => Ordering::Less, (_, Type::SubclassOf(_)) => Ordering::Greater, - (Type::NominalInstance(left), Type::NominalInstance(right)) => { - left.class().cmp(&right.class()) - } + (Type::NominalInstance(left), Type::NominalInstance(right)) => left.class.cmp(&right.class), (Type::NominalInstance(_), _) => Ordering::Less, (_, Type::NominalInstance(_)) => Ordering::Greater, @@ -171,7 +169,7 @@ pub(super) fn union_or_intersection_elements_ordering<'db>( (SuperOwnerKind::Class(_), _) => Ordering::Less, (_, SuperOwnerKind::Class(_)) => Ordering::Greater, (SuperOwnerKind::Instance(left), SuperOwnerKind::Instance(right)) => { - left.class().cmp(&right.class()) + left.class.cmp(&right.class) } (SuperOwnerKind::Instance(_), _) => Ordering::Less, (_, SuperOwnerKind::Instance(_)) => Ordering::Greater,