diff --git a/crates/red_knot_python_semantic/src/symbol.rs b/crates/red_knot_python_semantic/src/symbol.rs index 3be4ce8d02..b90103585a 100644 --- a/crates/red_knot_python_semantic/src/symbol.rs +++ b/crates/red_knot_python_semantic/src/symbol.rs @@ -1114,7 +1114,7 @@ mod tests { fn assert_bound_string_symbol<'db>(db: &'db dyn Db, symbol: Symbol<'db>) { assert!(matches!( symbol, - Symbol::Type(Type::Instance(_), Boundness::Bound) + Symbol::Type(Type::NominalInstance(_), Boundness::Bound) )); assert_eq!(symbol.expect_type(), KnownClass::Str.to_instance(db)); } diff --git a/crates/red_knot_python_semantic/src/types.rs b/crates/red_knot_python_semantic/src/types.rs index 070adc5aab..3204501846 100644 --- a/crates/red_knot_python_semantic/src/types.rs +++ b/crates/red_knot_python_semantic/src/types.rs @@ -50,7 +50,7 @@ pub(crate) use crate::types::narrow::infer_narrowing_constraint; use crate::types::signatures::{Parameter, ParameterForm, Parameters}; use crate::{Db, FxOrderSet, Module, Program}; pub(crate) use class::{ClassLiteral, ClassType, GenericAlias, KnownClass}; -pub(crate) use instance::InstanceType; +pub(crate) use instance::NominalInstanceType; pub(crate) use known_instance::KnownInstanceType; mod builder; @@ -475,7 +475,7 @@ pub enum Type<'db> { SubclassOf(SubclassOfType<'db>), /// The set of Python objects with the given class in their __class__'s method resolution order. /// Construct this variant using the `Type::instance` constructor function. - Instance(InstanceType<'db>), + NominalInstance(NominalInstanceType<'db>), /// A single Python object that requires special treatment in the type system KnownInstance(KnownInstanceType<'db>), /// An instance of `builtins.property` @@ -510,7 +510,7 @@ pub enum Type<'db> { TypeVar(TypeVarInstance<'db>), // A bound super object like `super()` or `super(A, A())` // This type doesn't handle an unbound super object like `super(A)`; for that we just use - // a `Type::Instance` of `builtins.super`. + // a `Type::NominalInstance` of `builtins.super`. BoundSuper(BoundSuperType<'db>), // TODO protocols, overloads, generics } @@ -583,7 +583,7 @@ impl<'db> Type<'db> { | Self::BooleanLiteral(_) | Self::BytesLiteral(_) | Self::FunctionLiteral(_) - | Self::Instance(_) + | Self::NominalInstance(_) | Self::ModuleLiteral(_) | Self::ClassLiteral(_) | Self::KnownInstance(_) @@ -888,7 +888,7 @@ impl<'db> Type<'db> { Type::Tuple(tuple) => Type::Tuple(tuple.normalized(db)), Type::Callable(callable) => Type::Callable(callable.normalized(db)), Type::LiteralString - | Type::Instance(_) + | Type::NominalInstance(_) | Type::PropertyInstance(_) | Type::AlwaysFalsy | Type::AlwaysTruthy @@ -980,7 +980,7 @@ impl<'db> Type<'db> { (_, Type::Never) => false, // Everything is a subtype of `object`. - (_, Type::Instance(instance)) if instance.class().is_object(db) => true, + (_, Type::NominalInstance(instance)) if instance.class().is_object(db) => true, // A fully static typevar is always a subtype of itself, and is never a subtype of any // other typevar, since there is no guarantee that they will be specialized to the same @@ -1232,7 +1232,7 @@ impl<'db> Type<'db> { metaclass_instance_type.is_subtype_of(db, target) }), - // For example: `Type::KnownInstance(KnownInstanceType::Type)` is a subtype of `Type::Instance(_SpecialForm)`, + // For example: `Type::KnownInstance(KnownInstanceType::Type)` is a subtype of `Type::NominalInstance(_SpecialForm)`, // because `Type::KnownInstance(KnownInstanceType::Type)` is a set with exactly one runtime value in it // (the symbol `typing.Type`), and that symbol is known to be an instance of `typing._SpecialForm` at runtime. (Type::KnownInstance(left), right) => { @@ -1241,11 +1241,11 @@ impl<'db> Type<'db> { // `bool` is a subtype of `int`, because `bool` subclasses `int`, // which means that all instances of `bool` are also instances of `int` - (Type::Instance(self_instance), Type::Instance(target_instance)) => { + (Type::NominalInstance(self_instance), Type::NominalInstance(target_instance)) => { self_instance.is_subtype_of(db, target_instance) } - (Type::Instance(_), Type::Callable(_)) => { + (Type::NominalInstance(_), Type::Callable(_)) => { let call_symbol = self.member(db, "__call__").symbol; match call_symbol { Symbol::Type(Type::BoundMethod(call_function), _) => call_function @@ -1264,7 +1264,7 @@ impl<'db> Type<'db> { // Other than the special cases enumerated above, `Instance` types and typevars are // never subtypes of any other variants - (Type::Instance(_) | Type::TypeVar(_), _) => false, + (Type::NominalInstance(_) | Type::TypeVar(_), _) => false, } } @@ -1286,7 +1286,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::Instance(instance)) if instance.class().is_object(db) => true, + (_, Type::NominalInstance(instance)) if instance.class().is_object(db) => true, // A typevar is always assignable to itself, and is never assignable to any other // typevar, since there is no guarantee that they will be specialized to the same @@ -1420,7 +1420,7 @@ impl<'db> Type<'db> { // subtypes of `type[object]` are `type[...]` types (or `Never`), and `type[Any]` can // materialize to any `type[...]` type (or to `type[Never]`, which is equivalent to // `Never`.) - (Type::SubclassOf(subclass_of_ty), Type::Instance(_)) + (Type::SubclassOf(subclass_of_ty), Type::NominalInstance(_)) if subclass_of_ty.is_dynamic() && (KnownClass::Type .to_instance(db) @@ -1432,7 +1432,7 @@ impl<'db> Type<'db> { // Any type that is assignable to `type[object]` is also assignable to `type[Any]`, // because `type[Any]` can materialize to `type[object]`. - (Type::Instance(_), Type::SubclassOf(subclass_of_ty)) + (Type::NominalInstance(_), Type::SubclassOf(subclass_of_ty)) if subclass_of_ty.is_dynamic() && self.is_assignable_to(db, KnownClass::Type.to_instance(db)) => { @@ -1441,11 +1441,11 @@ impl<'db> Type<'db> { // TODO: This is a workaround to avoid false positives (e.g. when checking function calls // with `SupportsIndex` parameters), which should be removed when we understand protocols. - (lhs, Type::Instance(instance)) + (lhs, Type::NominalInstance(instance)) if instance.class().is_known(db, KnownClass::SupportsIndex) => { match lhs { - Type::Instance(instance) + Type::NominalInstance(instance) if matches!( instance.class().known(db), Some(KnownClass::Int | KnownClass::SupportsIndex) @@ -1459,7 +1459,9 @@ impl<'db> Type<'db> { } // TODO: ditto for avoiding false positives when checking function calls with `Sized` parameters. - (lhs, Type::Instance(instance)) if instance.class().is_known(db, KnownClass::Sized) => { + (lhs, Type::NominalInstance(instance)) + if instance.class().is_known(db, KnownClass::Sized) => + { matches!( lhs.to_meta_type(db).member(db, "__len__"), SymbolAndQualifiers { @@ -1469,7 +1471,7 @@ impl<'db> Type<'db> { ) } - (Type::Instance(self_instance), Type::Instance(target_instance)) => { + (Type::NominalInstance(self_instance), Type::NominalInstance(target_instance)) => { self_instance.is_assignable_to(db, target_instance) } @@ -1477,7 +1479,7 @@ impl<'db> Type<'db> { self_callable.is_assignable_to(db, target_callable) } - (Type::Instance(_), Type::Callable(_)) => { + (Type::NominalInstance(_), Type::Callable(_)) => { let call_symbol = self.member(db, "__call__").symbol; match call_symbol { Symbol::Type(Type::BoundMethod(call_function), _) => call_function @@ -1517,7 +1519,9 @@ impl<'db> Type<'db> { } (Type::Tuple(left), Type::Tuple(right)) => left.is_equivalent_to(db, right), (Type::Callable(left), Type::Callable(right)) => left.is_equivalent_to(db, right), - (Type::Instance(left), Type::Instance(right)) => left.is_equivalent_to(db, right), + (Type::NominalInstance(left), Type::NominalInstance(right)) => { + left.is_equivalent_to(db, right) + } _ => self == other && self.is_fully_static(db) && other.is_fully_static(db), } } @@ -1552,7 +1556,7 @@ impl<'db> Type<'db> { (Type::TypeVar(first), Type::TypeVar(second)) => first == second, - (Type::Instance(first), Type::Instance(second)) => { + (Type::NominalInstance(first), Type::NominalInstance(second)) => { first.is_gradual_equivalent_to(db, second) } @@ -1780,8 +1784,8 @@ impl<'db> Type<'db> { .is_disjoint_from(db, other), }, - (Type::KnownInstance(known_instance), Type::Instance(instance)) - | (Type::Instance(instance), Type::KnownInstance(known_instance)) => { + (Type::KnownInstance(known_instance), Type::NominalInstance(instance)) + | (Type::NominalInstance(instance), Type::KnownInstance(known_instance)) => { !known_instance.is_instance_of(db, instance.class()) } @@ -1790,8 +1794,8 @@ impl<'db> Type<'db> { known_instance_ty.is_disjoint_from(db, KnownClass::Tuple.to_instance(db)) } - (Type::BooleanLiteral(..), Type::Instance(instance)) - | (Type::Instance(instance), Type::BooleanLiteral(..)) => { + (Type::BooleanLiteral(..), Type::NominalInstance(instance)) + | (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()) @@ -1799,8 +1803,8 @@ impl<'db> Type<'db> { (Type::BooleanLiteral(..), _) | (_, Type::BooleanLiteral(..)) => true, - (Type::IntLiteral(..), Type::Instance(instance)) - | (Type::Instance(instance), Type::IntLiteral(..)) => { + (Type::IntLiteral(..), Type::NominalInstance(instance)) + | (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()) @@ -1811,8 +1815,8 @@ impl<'db> Type<'db> { (Type::StringLiteral(..), Type::LiteralString) | (Type::LiteralString, Type::StringLiteral(..)) => false, - (Type::StringLiteral(..) | Type::LiteralString, Type::Instance(instance)) - | (Type::Instance(instance), Type::StringLiteral(..) | Type::LiteralString) => { + (Type::StringLiteral(..) | Type::LiteralString, Type::NominalInstance(instance)) + | (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()) @@ -1821,15 +1825,15 @@ impl<'db> Type<'db> { (Type::LiteralString, Type::LiteralString) => false, (Type::LiteralString, _) | (_, Type::LiteralString) => true, - (Type::BytesLiteral(..), Type::Instance(instance)) - | (Type::Instance(instance), Type::BytesLiteral(..)) => { + (Type::BytesLiteral(..), Type::NominalInstance(instance)) + | (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()) } - (Type::SliceLiteral(..), Type::Instance(instance)) - | (Type::Instance(instance), Type::SliceLiteral(..)) => { + (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()) @@ -1838,17 +1842,19 @@ impl<'db> Type<'db> { // 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. - (Type::ClassLiteral(class), instance @ Type::Instance(_)) - | (instance @ Type::Instance(_), Type::ClassLiteral(class)) => !class - .metaclass_instance_type(db) - .is_subtype_of(db, instance), - (Type::GenericAlias(alias), instance @ Type::Instance(_)) - | (instance @ Type::Instance(_), Type::GenericAlias(alias)) => !ClassType::from(alias) + (Type::ClassLiteral(class), instance @ Type::NominalInstance(_)) + | (instance @ Type::NominalInstance(_), Type::ClassLiteral(class)) => !class .metaclass_instance_type(db) .is_subtype_of(db, instance), + (Type::GenericAlias(alias), instance @ Type::NominalInstance(_)) + | (instance @ Type::NominalInstance(_), Type::GenericAlias(alias)) => { + !ClassType::from(alias) + .metaclass_instance_type(db) + .is_subtype_of(db, instance) + } - (Type::FunctionLiteral(..), Type::Instance(instance)) - | (Type::Instance(instance), Type::FunctionLiteral(..)) => { + (Type::FunctionLiteral(..), Type::NominalInstance(instance)) + | (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()) @@ -1904,13 +1910,15 @@ impl<'db> Type<'db> { false } - (Type::ModuleLiteral(..), other @ Type::Instance(..)) - | (other @ Type::Instance(..), Type::ModuleLiteral(..)) => { + (Type::ModuleLiteral(..), other @ Type::NominalInstance(..)) + | (other @ Type::NominalInstance(..), Type::ModuleLiteral(..)) => { // Modules *can* actually be instances of `ModuleType` subclasses other.is_disjoint_from(db, KnownClass::ModuleType.to_instance(db)) } - (Type::Instance(left), Type::Instance(right)) => left.is_disjoint_from(db, right), + (Type::NominalInstance(left), Type::NominalInstance(right)) => { + left.is_disjoint_from(db, right) + } (Type::Tuple(tuple), Type::Tuple(other_tuple)) => { let self_elements = tuple.elements(db); @@ -1922,8 +1930,8 @@ impl<'db> Type<'db> { .any(|(e1, e2)| e1.is_disjoint_from(db, *e2)) } - (Type::Tuple(..), instance @ Type::Instance(_)) - | (instance @ Type::Instance(_), Type::Tuple(..)) => { + (Type::Tuple(..), instance @ Type::NominalInstance(_)) + | (instance @ Type::NominalInstance(_), Type::Tuple(..)) => { // We cannot be sure if the tuple is disjoint from the instance because: // - 'other' might be the homogeneous arbitrary-length tuple type // tuple[T, ...] (which we don't have support for yet); if all of @@ -1983,7 +1991,7 @@ impl<'db> Type<'db> { !matches!(bound_super.pivot_class(db), ClassBase::Dynamic(_)) && !matches!(bound_super.owner(db), SuperOwnerKind::Dynamic(_)) } - Type::ClassLiteral(_) | Type::GenericAlias(_) | Type::Instance(_) => { + Type::ClassLiteral(_) | Type::GenericAlias(_) | Type::NominalInstance(_) => { // TODO: Ideally, we would iterate over the MRO of the class, check if all // bases are fully static, and only return `true` if that is the case. // @@ -2089,7 +2097,7 @@ impl<'db> Type<'db> { false } Type::DataclassDecorator(_) | Type::DataclassTransformer(_) => false, - Type::Instance(instance) => instance.is_singleton(db), + Type::NominalInstance(instance) => instance.is_singleton(db), Type::PropertyInstance(_) => false, Type::Tuple(..) => { // The empty tuple is a singleton on CPython and PyPy, but not on other Python @@ -2161,7 +2169,7 @@ impl<'db> Type<'db> { .iter() .all(|elem| elem.is_single_valued(db)), - Type::Instance(instance) => instance.is_single_valued(db), + Type::NominalInstance(instance) => instance.is_single_valued(db), Type::BoundSuper(_) => { // At runtime two super instances never compare equal, even if their arguments are identical. @@ -2299,10 +2307,11 @@ impl<'db> Type<'db> { .to_class_literal(db) .find_name_in_mro_with_policy(db, name, policy), - // We eagerly normalize type[object], i.e. Type::SubclassOf(object) to `type`, i.e. Type::Instance(type). - // So looking up a name in the MRO of `Type::Instance(type)` is equivalent to looking up the name in the + // We eagerly normalize type[object], i.e. Type::SubclassOf(object) to `type`, + // 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::Instance(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) @@ -2327,7 +2336,7 @@ impl<'db> Type<'db> { | Type::SliceLiteral(_) | Type::Tuple(_) | Type::TypeVar(_) - | Type::Instance(_) + | Type::NominalInstance(_) | Type::PropertyInstance(_) => None, } } @@ -2392,7 +2401,7 @@ impl<'db> Type<'db> { Type::Dynamic(_) | Type::Never => Symbol::bound(self).into(), - Type::Instance(instance) => instance.class().instance_member(db, name), + Type::NominalInstance(instance) => instance.class().instance_member(db, name), Type::FunctionLiteral(_) => KnownClass::FunctionType .to_instance(db) @@ -2833,7 +2842,7 @@ impl<'db> Type<'db> { .to_instance(db) .member_lookup_with_policy(db, name, policy), - Type::Instance(instance) + Type::NominalInstance(instance) if matches!(name.as_str(), "major" | "minor") && instance.class().is_known(db, KnownClass::VersionInfo) => { @@ -2875,7 +2884,7 @@ impl<'db> Type<'db> { policy, ), - Type::Instance(..) + Type::NominalInstance(..) | Type::BooleanLiteral(..) | Type::IntLiteral(..) | Type::StringLiteral(..) @@ -3167,7 +3176,7 @@ impl<'db> Type<'db> { } }, - Type::Instance(instance) => match instance.class().known(db) { + Type::NominalInstance(instance) => match instance.class().known(db) { Some(known_class) => known_class.bool(), None => try_dunder_bool()?, }, @@ -3911,7 +3920,7 @@ impl<'db> Type<'db> { SubclassOfInner::Class(class) => Type::from(class).signatures(db), }, - Type::Instance(_) => { + Type::NominalInstance(_) => { // Note that for objects that have a (possibly not callable!) `__call__` attribute, // we will get the signature of the `__call__` attribute, but will pass in the type // of the original object as the "callable type". That ensures that we get errors @@ -4361,7 +4370,7 @@ impl<'db> Type<'db> { | Type::WrapperDescriptor(_) | Type::DataclassDecorator(_) | Type::DataclassTransformer(_) - | Type::Instance(_) + | Type::NominalInstance(_) | Type::KnownInstance(_) | Type::PropertyInstance(_) | Type::ModuleLiteral(_) @@ -4381,7 +4390,7 @@ impl<'db> Type<'db> { /// /// For example, the builtin `int` as a value expression is of type /// `Type::ClassLiteral(builtins.int)`, that is, it is the `int` class itself. As a type - /// expression, it names the type `Type::Instance(builtins.int)`, that is, all objects whose + /// expression, it names the type `Type::NominalInstance(builtins.int)`, that is, all objects whose /// `__class__` is `int`. pub fn in_type_expression( &self, @@ -4558,7 +4567,7 @@ impl<'db> Type<'db> { Type::Dynamic(_) => Ok(*self), - Type::Instance(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" )), @@ -4634,7 +4643,7 @@ impl<'db> Type<'db> { pub fn to_meta_type(&self, db: &'db dyn Db) -> Type<'db> { match self { Type::Never => Type::Never, - Type::Instance(instance) => instance.to_meta_type(db), + Type::NominalInstance(instance) => instance.to_meta_type(db), Type::KnownInstance(known_instance) => known_instance.to_meta_type(db), Type::PropertyInstance(_) => KnownClass::Property.to_class_literal(db), Type::Union(union) => union.map(db, |ty| ty.to_meta_type(db)), @@ -4805,7 +4814,7 @@ impl<'db> Type<'db> { | Type::BoundSuper(_) // Instance contains a ClassType, which has already been specialized if needed, like // above with BoundMethod's self_instance. - | Type::Instance(_) + | Type::NominalInstance(_) | Type::KnownInstance(_) => self, } } @@ -4869,7 +4878,7 @@ impl<'db> Type<'db> { Some(TypeDefinition::Class(class_literal.definition(db))) } Self::GenericAlias(alias) => Some(TypeDefinition::Class(alias.definition(db))), - Self::Instance(instance) => { + Self::NominalInstance(instance) => { Some(TypeDefinition::Class(instance.class().definition(db))) } Self::KnownInstance(instance) => match instance { @@ -7457,7 +7466,7 @@ impl BoundSuperError<'_> { pub enum SuperOwnerKind<'db> { Dynamic(DynamicType), Class(ClassType<'db>), - Instance(InstanceType<'db>), + Instance(NominalInstanceType<'db>), } impl<'db> SuperOwnerKind<'db> { @@ -7491,7 +7500,7 @@ impl<'db> SuperOwnerKind<'db> { Type::ClassLiteral(class_literal) => Some(SuperOwnerKind::Class( class_literal.apply_optional_specialization(db, None), )), - Type::Instance(instance) => Some(SuperOwnerKind::Instance(instance)), + Type::NominalInstance(instance) => Some(SuperOwnerKind::Instance(instance)), Type::BooleanLiteral(_) => { SuperOwnerKind::try_from_type(db, KnownClass::Bool.to_instance(db)) } diff --git a/crates/red_knot_python_semantic/src/types/builder.rs b/crates/red_knot_python_semantic/src/types/builder.rs index 8f81db8d3f..005441f15a 100644 --- a/crates/red_knot_python_semantic/src/types/builder.rs +++ b/crates/red_knot_python_semantic/src/types/builder.rs @@ -611,7 +611,7 @@ impl<'db> InnerIntersectionBuilder<'db> { Type::AlwaysFalsy if addition_is_bool_instance => { new_positive = Type::BooleanLiteral(false); } - Type::Instance(instance) + Type::NominalInstance(instance) if instance.class().is_known(db, KnownClass::Bool) => { match new_positive { @@ -722,7 +722,7 @@ impl<'db> InnerIntersectionBuilder<'db> { Type::Never => { // Adding ~Never to an intersection is a no-op. } - Type::Instance(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/red_knot_python_semantic/src/types/class_base.rs b/crates/red_knot_python_semantic/src/types/class_base.rs index 395ff5268d..c801ce0076 100644 --- a/crates/red_knot_python_semantic/src/types/class_base.rs +++ b/crates/red_knot_python_semantic/src/types/class_base.rs @@ -78,12 +78,14 @@ impl<'db> ClassBase<'db> { Self::Class(literal.default_specialization(db)) }), Type::GenericAlias(generic) => Some(Self::Class(ClassType::Generic(generic))), - Type::Instance(instance) if instance.class().is_known(db, KnownClass::GenericAlias) => { + Type::NominalInstance(instance) + if instance.class().is_known(db, KnownClass::GenericAlias) => + { Self::try_from_type(db, todo_type!("GenericAlias instance")) } Type::Union(_) => None, // TODO -- forces consideration of multiple possible MROs? Type::Intersection(_) => None, // TODO -- probably incorrect? - Type::Instance(_) => None, // TODO -- handle `__mro_entries__`? + Type::NominalInstance(_) => None, // TODO -- handle `__mro_entries__`? Type::PropertyInstance(_) => None, Type::Never | Type::BooleanLiteral(_) diff --git a/crates/red_knot_python_semantic/src/types/display.rs b/crates/red_knot_python_semantic/src/types/display.rs index 4220b0cf41..17c380a601 100644 --- a/crates/red_knot_python_semantic/src/types/display.rs +++ b/crates/red_knot_python_semantic/src/types/display.rs @@ -73,12 +73,14 @@ impl Display for DisplayRepresentation<'_> { match self.ty { Type::Dynamic(dynamic) => dynamic.fmt(f), Type::Never => f.write_str("Never"), - Type::Instance(instance) => 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)), - (ClassType::Generic(alias), _) => write!(f, "{}", alias.display(self.db)), - }, + Type::NominalInstance(instance) => { + 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)), + (ClassType::Generic(alias), _) => write!(f, "{}", alias.display(self.db)), + } + } Type::PropertyInstance(_) => f.write_str("property"), Type::ModuleLiteral(module) => { write!(f, "", module.module(self.db).name()) diff --git a/crates/red_knot_python_semantic/src/types/infer.rs b/crates/red_knot_python_semantic/src/types/infer.rs index 29d69e5099..4af0a8ccff 100644 --- a/crates/red_knot_python_semantic/src/types/infer.rs +++ b/crates/red_knot_python_semantic/src/types/infer.rs @@ -1064,7 +1064,7 @@ impl<'db> TypeInferenceBuilder<'db> { ) -> bool { match left { Type::BooleanLiteral(_) | Type::IntLiteral(_) => {} - Type::Instance(instance) + Type::NominalInstance(instance) if matches!( instance.class().known(self.db()), Some(KnownClass::Float | KnownClass::Int | KnownClass::Bool) @@ -2515,7 +2515,7 @@ impl<'db> TypeInferenceBuilder<'db> { } // Super instances do not allow attribute assignment - Type::Instance(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!( @@ -2540,7 +2540,7 @@ impl<'db> TypeInferenceBuilder<'db> { Type::Dynamic(..) | Type::Never => true, - Type::Instance(..) + Type::NominalInstance(..) | Type::BooleanLiteral(..) | Type::IntLiteral(..) | Type::StringLiteral(..) @@ -3031,7 +3031,7 @@ impl<'db> TypeInferenceBuilder<'db> { } // Handle various singletons. - if let Type::Instance(instance) = declared_ty.inner_type() { + if let Type::NominalInstance(instance) = declared_ty.inner_type() { if instance .class() .is_known(self.db(), KnownClass::SpecialForm) @@ -5007,7 +5007,7 @@ impl<'db> TypeInferenceBuilder<'db> { | Type::ClassLiteral(_) | Type::GenericAlias(_) | Type::SubclassOf(_) - | Type::Instance(_) + | Type::NominalInstance(_) | Type::KnownInstance(_) | Type::PropertyInstance(_) | Type::Union(_) @@ -5287,7 +5287,7 @@ impl<'db> TypeInferenceBuilder<'db> { | Type::ClassLiteral(_) | Type::GenericAlias(_) | Type::SubclassOf(_) - | Type::Instance(_) + | Type::NominalInstance(_) | Type::KnownInstance(_) | Type::PropertyInstance(_) | Type::Intersection(_) @@ -5312,7 +5312,7 @@ impl<'db> TypeInferenceBuilder<'db> { | Type::ClassLiteral(_) | Type::GenericAlias(_) | Type::SubclassOf(_) - | Type::Instance(_) + | Type::NominalInstance(_) | Type::KnownInstance(_) | Type::PropertyInstance(_) | Type::Intersection(_) @@ -5745,13 +5745,13 @@ impl<'db> TypeInferenceBuilder<'db> { right_ty: right, }), }, - (Type::IntLiteral(_), Type::Instance(_)) => self.infer_binary_type_comparison( + (Type::IntLiteral(_), Type::NominalInstance(_)) => self.infer_binary_type_comparison( KnownClass::Int.to_instance(self.db()), op, right, range, ), - (Type::Instance(_), Type::IntLiteral(_)) => self.infer_binary_type_comparison( + (Type::NominalInstance(_), Type::IntLiteral(_)) => self.infer_binary_type_comparison( left, op, KnownClass::Int.to_instance(self.db()), @@ -5877,7 +5877,7 @@ impl<'db> TypeInferenceBuilder<'db> { KnownClass::Bytes.to_instance(self.db()), range, ), - (Type::Tuple(_), Type::Instance(instance)) + (Type::Tuple(_), Type::NominalInstance(instance)) if instance .class() .is_known(self.db(), KnownClass::VersionInfo) => @@ -5889,7 +5889,7 @@ impl<'db> TypeInferenceBuilder<'db> { range, ) } - (Type::Instance(instance), Type::Tuple(_)) + (Type::NominalInstance(instance), Type::Tuple(_)) if instance .class() .is_known(self.db(), KnownClass::VersionInfo) => @@ -6275,7 +6275,7 @@ impl<'db> TypeInferenceBuilder<'db> { ) -> Type<'db> { match (value_ty, slice_ty) { ( - Type::Instance(instance), + Type::NominalInstance(instance), Type::IntLiteral(_) | Type::BooleanLiteral(_) | Type::SliceLiteral(_), ) if instance .class() @@ -6576,7 +6576,7 @@ impl<'db> TypeInferenceBuilder<'db> { Err(_) => SliceArg::Unsupported, }, Some(Type::BooleanLiteral(b)) => SliceArg::Arg(Some(i32::from(b))), - Some(Type::Instance(instance)) + Some(Type::NominalInstance(instance)) if instance.class().is_known(self.db(), KnownClass::NoneType) => { SliceArg::Arg(None) diff --git a/crates/red_knot_python_semantic/src/types/instance.rs b/crates/red_knot_python_semantic/src/types/instance.rs index fb1b1deb76..e6e9785e16 100644 --- a/crates/red_knot_python_semantic/src/types/instance.rs +++ b/crates/red_knot_python_semantic/src/types/instance.rs @@ -5,12 +5,12 @@ use crate::Db; impl<'db> Type<'db> { pub(crate) const fn instance(class: ClassType<'db>) -> Self { - Self::Instance(InstanceType { class }) + Self::NominalInstance(NominalInstanceType { class }) } - pub(crate) const fn into_instance(self) -> Option> { + pub(crate) const fn into_instance(self) -> Option> { match self { - Type::Instance(instance_type) => Some(instance_type), + Type::NominalInstance(instance_type) => Some(instance_type), _ => None, } } @@ -18,13 +18,13 @@ 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 InstanceType<'db> { - // Keep this field private, so that the only way of constructing `InstanceType` instances +pub struct NominalInstanceType<'db> { + // Keep this field private, so that the only way of constructing `NominalInstanceType` instances // is through the `Type::instance` constructor function. class: ClassType<'db>, } -impl<'db> InstanceType<'db> { +impl<'db> NominalInstanceType<'db> { pub(super) fn class(self) -> ClassType<'db> { self.class } @@ -87,8 +87,8 @@ impl<'db> InstanceType<'db> { } } -impl<'db> From> for Type<'db> { - fn from(value: InstanceType<'db>) -> Self { - Self::Instance(value) +impl<'db> From> for Type<'db> { + fn from(value: NominalInstanceType<'db>) -> Self { + Self::NominalInstance(value) } } diff --git a/crates/red_knot_python_semantic/src/types/known_instance.rs b/crates/red_knot_python_semantic/src/types/known_instance.rs index 27ed7a533a..0a125f933e 100644 --- a/crates/red_knot_python_semantic/src/types/known_instance.rs +++ b/crates/red_knot_python_semantic/src/types/known_instance.rs @@ -1,6 +1,6 @@ //! The `KnownInstance` type. //! -//! Despite its name, this is quite a different type from [`super::InstanceType`]. +//! Despite its name, this is quite a different type from [`super::NominalInstanceType`]. //! For the vast majority of instance-types in Python, we cannot say how many possible //! inhabitants there are or could be of that type at runtime. Each variant of the //! [`KnownInstanceType`] enum, however, represents a specific runtime symbol @@ -249,7 +249,7 @@ impl<'db> KnownInstanceType<'db> { /// /// For example, the symbol `typing.Literal` is an instance of `typing._SpecialForm`, /// so `KnownInstanceType::Literal.instance_fallback(db)` - /// returns `Type::Instance(InstanceType { class: })`. + /// returns `Type::NominalInstance(NominalInstanceType { class: })`. pub(super) fn instance_fallback(self, db: &dyn Db) -> Type { self.class().to_instance(db) } diff --git a/crates/red_knot_python_semantic/src/types/narrow.rs b/crates/red_knot_python_semantic/src/types/narrow.rs index 19cb5c2e5d..902a0042e2 100644 --- a/crates/red_knot_python_semantic/src/types/narrow.rs +++ b/crates/red_knot_python_semantic/src/types/narrow.rs @@ -472,7 +472,7 @@ impl<'db> NarrowingConstraintsBuilder<'db> { union.map(db, |ty| filter_to_cannot_be_equal(db, *ty, rhs_ty)) } // Treat `bool` as `Literal[True, False]`. - Type::Instance(instance) if instance.class().is_known(db, KnownClass::Bool) => { + Type::NominalInstance(instance) if instance.class().is_known(db, KnownClass::Bool) => { UnionType::from_elements( db, [Type::BooleanLiteral(true), Type::BooleanLiteral(false)] @@ -501,7 +501,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::Instance(instance), Type::IntLiteral(i)) + (Type::NominalInstance(instance), Type::IntLiteral(i)) if instance.class().is_known(self.db, KnownClass::Bool) => { if i == 0 { diff --git a/crates/red_knot_python_semantic/src/types/subclass_of.rs b/crates/red_knot_python_semantic/src/types/subclass_of.rs index 52dd82aaa6..c3ae9a5c02 100644 --- a/crates/red_knot_python_semantic/src/types/subclass_of.rs +++ b/crates/red_knot_python_semantic/src/types/subclass_of.rs @@ -16,8 +16,8 @@ impl<'db> SubclassOfType<'db> { /// This method does not always return a [`Type::SubclassOf`] variant. /// If the class object is known to be a final class, /// this method will return a [`Type::ClassLiteral`] variant; this is a more precise type. - /// If the class object is `builtins.object`, `Type::Instance()` will be returned; - /// this is no more precise, but it is exactly equivalent to `type[object]`. + /// If the class object is `builtins.object`, `Type::NominalInstance()` + /// will be returned; this is no more precise, but it is exactly equivalent to `type[object]`. /// /// The eager normalization here means that we do not need to worry elsewhere about distinguishing /// between `@final` classes and other classes when dealing with [`Type::SubclassOf`] variants. diff --git a/crates/red_knot_python_semantic/src/types/type_ordering.rs b/crates/red_knot_python_semantic/src/types/type_ordering.rs index 8037611f51..b1c5d46510 100644 --- a/crates/red_knot_python_semantic/src/types/type_ordering.rs +++ b/crates/red_knot_python_semantic/src/types/type_ordering.rs @@ -126,10 +126,12 @@ pub(super) fn union_or_intersection_elements_ordering<'db>( (Type::SubclassOf(_), _) => Ordering::Less, (_, Type::SubclassOf(_)) => Ordering::Greater, - (Type::Instance(left), Type::Instance(right)) => left.class().cmp(&right.class()), + (Type::NominalInstance(left), Type::NominalInstance(right)) => { + left.class().cmp(&right.class()) + } - (Type::Instance(_), _) => Ordering::Less, - (_, Type::Instance(_)) => Ordering::Greater, + (Type::NominalInstance(_), _) => Ordering::Less, + (_, Type::NominalInstance(_)) => Ordering::Greater, (Type::TypeVar(left), Type::TypeVar(right)) => left.cmp(right), (Type::TypeVar(_), _) => Ordering::Less,