Extract class and instance types (#16337)

This commit is contained in:
Micha Reiser
2025-02-24 11:36:20 +00:00
committed by GitHub
parent 24e08d17c4
commit 320a3c68ae
7 changed files with 1565 additions and 1528 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -26,7 +26,7 @@
//! eliminate the supertype from the intersection).
//! * An intersection containing two non-overlapping types should simplify to [`Type::Never`].
use crate::types::{InstanceType, IntersectionType, KnownClass, Type, UnionType};
use crate::types::{IntersectionType, KnownClass, Type, UnionType};
use crate::{Db, FxOrderSet};
use smallvec::SmallVec;
@@ -299,7 +299,7 @@ impl<'db> InnerIntersectionBuilder<'db> {
_ => {
let known_instance = new_positive
.into_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
@@ -318,8 +318,8 @@ impl<'db> InnerIntersectionBuilder<'db> {
Type::AlwaysFalsy if addition_is_bool_instance => {
new_positive = Type::BooleanLiteral(false);
}
Type::Instance(InstanceType { class })
if class.is_known(db, KnownClass::Bool) =>
Type::Instance(instance)
if instance.class().is_known(db, KnownClass::Bool) =>
{
match new_positive {
// `bool & AlwaysTruthy` -> `Literal[True]`
@@ -413,7 +413,7 @@ impl<'db> InnerIntersectionBuilder<'db> {
self.positive
.iter()
.filter_map(|ty| ty.into_instance())
.filter_map(|instance| instance.class.known(db))
.filter_map(|instance| instance.class().known(db))
.any(KnownClass::is_bool)
};
@@ -429,7 +429,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::Instance(instance) if instance.class().is_object(db) => {
// Adding ~object to an intersection results in Never.
*self = Self::default();
self.positive.insert(Type::Never);

View File

@@ -203,7 +203,7 @@ impl<'db> CallBinding<'db> {
fn callable_name(&self, db: &'db dyn Db) -> Option<&str> {
match self.callable_ty {
Type::FunctionLiteral(function) => Some(function.name(db)),
Type::ClassLiteral(class_type) => Some(class_type.class.name(db)),
Type::ClassLiteral(class_type) => Some(class_type.class().name(db)),
_ => None,
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,4 @@
use crate::types::{
todo_type, Class, ClassLiteralType, DynamicType, KnownClass, KnownInstanceType, Type,
};
use crate::types::{todo_type, Class, DynamicType, KnownClass, KnownInstanceType, Type};
use crate::Db;
use itertools::Either;
@@ -54,9 +52,7 @@ impl<'db> ClassBase<'db> {
KnownClass::Object
.to_class_literal(db)
.into_class_literal()
.map_or(Self::unknown(), |ClassLiteralType { class }| {
Self::Class(class)
})
.map_or(Self::unknown(), |literal| Self::Class(literal.class()))
}
/// Attempt to resolve `ty` into a `ClassBase`.
@@ -65,7 +61,7 @@ impl<'db> ClassBase<'db> {
pub(super) fn try_from_type(db: &'db dyn Db, ty: Type<'db>) -> Option<Self> {
match ty {
Type::Dynamic(dynamic) => Some(Self::Dynamic(dynamic)),
Type::ClassLiteral(ClassLiteralType { class }) => Some(Self::Class(class)),
Type::ClassLiteral(literal) => Some(Self::Class(literal.class())),
Type::Union(_) => None, // TODO -- forces consideration of multiple possible MROs?
Type::Intersection(_) => None, // TODO -- probably incorrect?
Type::Instance(_) => None, // TODO -- handle `__mro_entries__`?

View File

@@ -68,9 +68,9 @@ use crate::types::diagnostic::{
use crate::types::mro::MroErrorKind;
use crate::types::unpacker::{UnpackResult, Unpacker};
use crate::types::{
todo_type, Boundness, Class, ClassLiteralType, DynamicType, FunctionType, InstanceType,
IntersectionBuilder, IntersectionType, KnownClass, KnownFunction, KnownInstanceType,
MetaclassCandidate, MetaclassErrorKind, SliceLiteralType, SubclassOfType, Symbol,
class::MetaclassErrorKind, todo_type, Boundness, Class, DynamicType, FunctionType,
InstanceType, IntersectionBuilder, IntersectionType, KnownClass, KnownFunction,
KnownInstanceType, MetaclassCandidate, SliceLiteralType, SubclassOfType, Symbol,
SymbolAndQualifiers, Truthiness, TupleType, Type, TypeAliasType, TypeAndQualifiers,
TypeArrayDisplay, TypeQualifiers, TypeVarBoundOrConstraints, TypeVarInstance, UnionBuilder,
UnionType,
@@ -607,7 +607,7 @@ impl<'db> TypeInferenceBuilder<'db> {
if let DefinitionKind::Class(class) = definition.kind(self.db()) {
ty.inner_type()
.into_class_literal()
.map(|ty| (ty.class, class.node()))
.map(|ty| (ty.class(), class.node()))
} else {
None
}
@@ -635,7 +635,9 @@ impl<'db> TypeInferenceBuilder<'db> {
// (2) Check for classes that inherit from `@final` classes
for (i, base_class) in class.explicit_bases(self.db()).iter().enumerate() {
// dynamic/unknown bases are never `@final`
let Some(ClassLiteralType { class: base_class }) = base_class.into_class_literal()
let Some(base_class) = base_class
.into_class_literal()
.map(super::class::ClassLiteralType::class)
else {
continue;
};
@@ -875,9 +877,9 @@ impl<'db> TypeInferenceBuilder<'db> {
fn check_division_by_zero(&mut self, expr: &ast::ExprBinOp, left: Type<'db>) {
match left {
Type::BooleanLiteral(_) | Type::IntLiteral(_) => {}
Type::Instance(InstanceType { class })
Type::Instance(instance)
if matches!(
class.known(self.db()),
instance.class().known(self.db()),
Some(KnownClass::Float | KnownClass::Int | KnownClass::Bool)
) => {}
_ => return,
@@ -2191,8 +2193,11 @@ impl<'db> TypeInferenceBuilder<'db> {
);
// Handle various singletons.
if let Type::Instance(InstanceType { class }) = declared_ty.inner_type() {
if class.is_known(self.db(), KnownClass::SpecialForm) {
if let Type::Instance(instance) = declared_ty.inner_type() {
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(),
@@ -2251,9 +2256,10 @@ impl<'db> TypeInferenceBuilder<'db> {
self.infer_augmented_op(assignment, target_type, value_type)
})
}
Type::Instance(InstanceType { class }) => {
if let Symbol::Type(class_member, boundness) =
class.class_member(self.db(), op.in_place_dunder())
Type::Instance(instance) => {
if let Symbol::Type(class_member, boundness) = instance
.class()
.class_member(self.db(), op.in_place_dunder())
{
let call = class_member.try_call(
self.db(),
@@ -3746,7 +3752,7 @@ impl<'db> TypeInferenceBuilder<'db> {
let value_ty = self.infer_expression(value);
let symbol = if let Type::Instance(instance) = value_ty {
let instance_member = instance.class.instance_member(self.db(), attr);
let instance_member = instance.class().instance_member(self.db(), attr);
if instance_member.is_class_var() {
self.context.report_lint(
&INVALID_ATTRIBUTE_ACCESS,
@@ -4622,8 +4628,10 @@ impl<'db> TypeInferenceBuilder<'db> {
KnownClass::Bytes.to_instance(self.db()),
range,
),
(Type::Tuple(_), Type::Instance(InstanceType { class }))
if class.is_known(self.db(), KnownClass::VersionInfo) =>
(Type::Tuple(_), Type::Instance(instance))
if instance
.class()
.is_known(self.db(), KnownClass::VersionInfo) =>
{
self.infer_binary_type_comparison(
left,
@@ -4632,8 +4640,10 @@ impl<'db> TypeInferenceBuilder<'db> {
range,
)
}
(Type::Instance(InstanceType { class }), Type::Tuple(_))
if class.is_known(self.db(), KnownClass::VersionInfo) =>
(Type::Instance(instance), Type::Tuple(_))
if instance
.class()
.is_known(self.db(), KnownClass::VersionInfo) =>
{
self.infer_binary_type_comparison(
Type::version_info_tuple(self.db()),
@@ -4778,7 +4788,7 @@ impl<'db> TypeInferenceBuilder<'db> {
let call_dunder = |op: RichCompareOperator,
left: InstanceType<'db>,
right: InstanceType<'db>| {
match left.class.class_member(db, op.dunder()) {
match left.class().class_member(db, op.dunder()) {
Symbol::Type(class_member_dunder, Boundness::Bound) => class_member_dunder
.try_call(
db,
@@ -4826,7 +4836,7 @@ impl<'db> TypeInferenceBuilder<'db> {
) -> Result<Type<'db>, CompareUnsupportedError<'db>> {
let db = self.db();
let contains_dunder = right.class.class_member(db, "__contains__");
let contains_dunder = right.class().class_member(db, "__contains__");
let compare_result_opt = match contains_dunder {
Symbol::Type(contains_dunder, Boundness::Bound) => {
// If `__contains__` is available, it is used directly for the membership test.
@@ -4971,14 +4981,18 @@ impl<'db> TypeInferenceBuilder<'db> {
) -> Type<'db> {
match (value_ty, slice_ty) {
(
Type::Instance(InstanceType { class }),
Type::Instance(instance),
Type::IntLiteral(_) | Type::BooleanLiteral(_) | Type::SliceLiteral(_),
) if class.is_known(self.db(), KnownClass::VersionInfo) => self
.infer_subscript_expression_types(
) if instance
.class()
.is_known(self.db(), KnownClass::VersionInfo) =>
{
self.infer_subscript_expression_types(
value_node,
Type::version_info_tuple(self.db()),
slice_ty,
),
)
}
// Ex) Given `("a", "b", "c", "d")[1]`, return `"b"`
(Type::Tuple(tuple_ty), Type::IntLiteral(int)) if i32::try_from(int).is_ok() => {
@@ -5175,7 +5189,7 @@ impl<'db> TypeInferenceBuilder<'db> {
}
}
if matches!(value_ty, Type::ClassLiteral(ClassLiteralType { class }) if class.is_known(self.db(), KnownClass::Type))
if matches!(value_ty, Type::ClassLiteral(class_literal) if class_literal.class().is_known(self.db(), KnownClass::Type))
{
return KnownClass::GenericAlias.to_instance(self.db());
}
@@ -5223,8 +5237,8 @@ impl<'db> TypeInferenceBuilder<'db> {
Err(_) => SliceArg::Unsupported,
},
Some(Type::BooleanLiteral(b)) => SliceArg::Arg(Some(i32::from(b))),
Some(Type::Instance(InstanceType { class }))
if class.is_known(self.db(), KnownClass::NoneType) =>
Some(Type::Instance(instance))
if instance.class().is_known(self.db(), KnownClass::NoneType) =>
{
SliceArg::Arg(None)
}
@@ -5661,7 +5675,8 @@ impl<'db> TypeInferenceBuilder<'db> {
value_ty: Type<'db>,
) -> Type<'db> {
match value_ty {
Type::ClassLiteral(class_literal_ty) => match class_literal_ty.class.known(self.db()) {
Type::ClassLiteral(class_literal_ty) => match class_literal_ty.class().known(self.db())
{
Some(KnownClass::Tuple) => self.infer_tuple_type_expression(slice),
Some(KnownClass::Type) => self.infer_subclass_of_type_expression(slice),
_ => self.infer_subscript_type_expression(subscript, value_ty),
@@ -5750,8 +5765,8 @@ impl<'db> TypeInferenceBuilder<'db> {
ast::Expr::Name(_) | ast::Expr::Attribute(_) => {
let name_ty = self.infer_expression(slice);
match name_ty {
Type::ClassLiteral(ClassLiteralType { class }) => {
SubclassOfType::from(self.db(), class)
Type::ClassLiteral(class_literal_ty) => {
SubclassOfType::from(self.db(), class_literal_ty.class())
}
Type::KnownInstance(KnownInstanceType::Any) => {
SubclassOfType::subclass_of_any()

View File

@@ -8,8 +8,8 @@ use crate::semantic_index::symbol::{ScopeId, ScopedSymbolId, SymbolTable};
use crate::semantic_index::symbol_table;
use crate::types::infer::infer_same_file_expression_type;
use crate::types::{
infer_expression_types, ClassLiteralType, IntersectionBuilder, KnownClass, KnownFunction,
SubclassOfType, Truthiness, Type, UnionBuilder,
infer_expression_types, IntersectionBuilder, KnownClass, KnownFunction, SubclassOfType,
Truthiness, Type, UnionBuilder,
};
use crate::Db;
use itertools::Itertools;
@@ -111,7 +111,7 @@ impl KnownConstraintFunction {
}
Some(builder.build())
}
Type::ClassLiteral(ClassLiteralType { class }) => Some(constraint_fn(class)),
Type::ClassLiteral(class_literal) => Some(constraint_fn(class_literal.class())),
Type::SubclassOf(subclass_of_ty) => {
subclass_of_ty.subclass_of().into_class().map(constraint_fn)
}
@@ -398,7 +398,7 @@ impl<'db> NarrowingConstraintsBuilder<'db> {
if callable_ty
.into_class_literal()
.is_some_and(|c| c.class.is_known(self.db, KnownClass::Type))
.is_some_and(|c| c.class().is_known(self.db, KnownClass::Type))
{
let symbol = self
.symbols()
@@ -456,7 +456,7 @@ impl<'db> NarrowingConstraintsBuilder<'db> {
Type::ClassLiteral(class_type)
if expr_call.arguments.args.len() == 1
&& expr_call.arguments.keywords.is_empty()
&& class_type.class.is_known(self.db, KnownClass::Bool) =>
&& class_type.class().is_known(self.db, KnownClass::Bool) =>
{
self.evaluate_expression_node_constraint(
&expr_call.arguments.args[0],