Skip to content

Commit

Permalink
better docs and more constructor methods
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexWaygood committed Jan 7, 2025
1 parent a83b8e8 commit 6baefda
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 9 deletions.
19 changes: 12 additions & 7 deletions crates/red_knot_python_semantic/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1167,6 +1167,10 @@ impl<'db> Type<'db> {
}

(Type::SubclassOf(_), other) | (other, Type::SubclassOf(_)) => {
// TODO we could do better here: if both variants are `SubclassOf` and they have different "solid bases",
// multiple inheritance between the two is impossible, so they are disjoint.
//
// Note that `type[<@final class>]` is eagerly simplified to `Literal[<@final class>]` by [`SubclassOfType::from`].
other.is_disjoint_from(db, KnownClass::Type.to_instance(db))
}

Expand Down Expand Up @@ -2114,8 +2118,8 @@ impl<'db> Type<'db> {
},

Type::StringLiteral(_) | Type::LiteralString => KnownClass::Str.to_class_literal(db),
Type::Any => SubclassOfType::from(db, ClassBase::Any),
Type::Unknown => SubclassOfType::from(db, ClassBase::Unknown),
Type::Any => SubclassOfType::subclass_of_any(),
Type::Unknown => SubclassOfType::subclass_of_unknown(),
// TODO intersections
Type::Intersection(_) => SubclassOfType::from(
db,
Expand Down Expand Up @@ -2333,7 +2337,7 @@ impl<'db> KnownClass {
self.to_class_literal(db)
.into_class_literal()
.map(|ClassLiteralType { class }| SubclassOfType::from(db, class))
.unwrap_or_else(|| SubclassOfType::from(db, ClassBase::Unknown))
.unwrap_or_else(SubclassOfType::subclass_of_unknown)
}

/// Return `true` if this symbol can be resolved to a class definition `class` in typeshed,
Expand Down Expand Up @@ -2731,6 +2735,7 @@ impl<'db> KnownInstanceType<'db> {
self.class().to_instance(db)
}

/// Return `true` if this symbol is an instance of `class`.
pub fn is_instance_of(self, db: &'db dyn Db, class: Class<'db>) -> bool {
self.class().is_subclass_of(db, class)
}
Expand Down Expand Up @@ -3324,7 +3329,7 @@ impl<'db> Class<'db> {
/// Return the metaclass of this class, or `type[Unknown]` if the metaclass cannot be inferred.
pub(crate) fn metaclass(self, db: &'db dyn Db) -> Type<'db> {
self.try_metaclass(db)
.unwrap_or_else(|_| SubclassOfType::from(db, ClassBase::Unknown))
.unwrap_or_else(|_| SubclassOfType::subclass_of_unknown())
}

/// Return the metaclass of this class, or an error if the metaclass cannot be inferred.
Expand All @@ -3337,7 +3342,7 @@ impl<'db> Class<'db> {
// We emit diagnostics for cyclic class definitions elsewhere.
// Avoid attempting to infer the metaclass if the class is cyclically defined:
// it would be easy to enter an infinite loop.
return Ok(SubclassOfType::from(db, ClassBase::Unknown));
return Ok(SubclassOfType::subclass_of_unknown());
}

let explicit_metaclass = self.explicit_metaclass(db);
Expand Down Expand Up @@ -3786,8 +3791,8 @@ pub(crate) mod tests {
let elements = tys.into_iter().map(|ty| ty.into_type(db));
TupleType::from_elements(db, elements)
}
Ty::SubclassOfAny => SubclassOfType::from(db, ClassBase::Any),
Ty::SubclassOfUnknown => SubclassOfType::from(db, ClassBase::Unknown),
Ty::SubclassOfAny => SubclassOfType::subclass_of_any(),
Ty::SubclassOfUnknown => SubclassOfType::subclass_of_unknown(),
Ty::SubclassOfBuiltinClass(s) => SubclassOfType::from(
db,
builtins_symbol(db, s)
Expand Down
3 changes: 1 addition & 2 deletions crates/red_knot_python_semantic/src/types/infer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ use crate::semantic_index::semantic_index;
use crate::semantic_index::symbol::{NodeWithScopeKind, NodeWithScopeRef, ScopeId};
use crate::semantic_index::SemanticIndex;
use crate::stdlib::builtins_module_scope;
use crate::types::class_base::ClassBase;
use crate::types::diagnostic::{
report_invalid_assignment, report_unresolved_module, TypeCheckDiagnostics, CALL_NON_CALLABLE,
CALL_POSSIBLY_UNBOUND_METHOD, CONFLICTING_DECLARATIONS, CONFLICTING_METACLASS,
Expand Down Expand Up @@ -4895,7 +4894,7 @@ impl<'db> TypeInferenceBuilder<'db> {
SubclassOfType::from(self.db(), class)
}
Type::KnownInstance(KnownInstanceType::Any) => {
SubclassOfType::from(self.db(), ClassBase::Any)
SubclassOfType::subclass_of_any()
}
_ => todo_type!("unsupported type[X] special form"),
}
Expand Down
30 changes: 30 additions & 0 deletions crates/red_knot_python_semantic/src/types/subclass_of.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,17 @@ pub struct SubclassOfType<'db> {
}

impl<'db> SubclassOfType<'db> {
/// Construct a new [`Type`] instance representing a given class object (or a given dynamic type)
/// and all possible subclasses of that class object/dynamic type.
///
/// 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(<builtins.type>)` 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.
pub fn from(db: &'db dyn Db, subclass_of: impl Into<ClassBase<'db>>) -> Type<'db> {
let subclass_of = subclass_of.into();
match subclass_of {
Expand All @@ -26,6 +37,21 @@ impl<'db> SubclassOfType<'db> {
}
}

/// Return a [`Type`] instance representing the type `type[Unknown]`.
pub const fn subclass_of_unknown() -> Type<'db> {
Type::SubclassOf(SubclassOfType {
subclass_of: ClassBase::Unknown,
})
}

/// Return a [`Type`] instance representing the type `type[Any]`.
pub const fn subclass_of_any() -> Type<'db> {
Type::SubclassOf(SubclassOfType {
subclass_of: ClassBase::Any,
})
}

/// Return the inner `ClassBase` value wrapped by this `SubclassOfType`.
pub const fn subclass_of(self) -> ClassBase<'db> {
self.subclass_of
}
Expand All @@ -44,6 +70,10 @@ impl<'db> SubclassOfType<'db> {
Type::from(self.subclass_of).member(db, name)
}

/// Return `true` if `self` is a subtype of `other`.
///
/// This can only return `true` if `self.subclass_of` is a [`ClassBase::Class`] variant;
/// only fully static types participate in subtyping.
pub fn is_subtype_of(self, db: &'db dyn Db, other: SubclassOfType<'db>) -> bool {
match (self.subclass_of, other.subclass_of) {
// Non-fully-static types do not participate in subtyping
Expand Down

0 comments on commit 6baefda

Please sign in to comment.