Skip to content

Commit 6baefda

Browse files
committed
better docs and more constructor methods
1 parent a83b8e8 commit 6baefda

File tree

3 files changed

+43
-9
lines changed

3 files changed

+43
-9
lines changed

crates/red_knot_python_semantic/src/types.rs

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1167,6 +1167,10 @@ impl<'db> Type<'db> {
11671167
}
11681168

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

@@ -2114,8 +2118,8 @@ impl<'db> Type<'db> {
21142118
},
21152119

21162120
Type::StringLiteral(_) | Type::LiteralString => KnownClass::Str.to_class_literal(db),
2117-
Type::Any => SubclassOfType::from(db, ClassBase::Any),
2118-
Type::Unknown => SubclassOfType::from(db, ClassBase::Unknown),
2121+
Type::Any => SubclassOfType::subclass_of_any(),
2122+
Type::Unknown => SubclassOfType::subclass_of_unknown(),
21192123
// TODO intersections
21202124
Type::Intersection(_) => SubclassOfType::from(
21212125
db,
@@ -2333,7 +2337,7 @@ impl<'db> KnownClass {
23332337
self.to_class_literal(db)
23342338
.into_class_literal()
23352339
.map(|ClassLiteralType { class }| SubclassOfType::from(db, class))
2336-
.unwrap_or_else(|| SubclassOfType::from(db, ClassBase::Unknown))
2340+
.unwrap_or_else(SubclassOfType::subclass_of_unknown)
23372341
}
23382342

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

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

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

33433348
let explicit_metaclass = self.explicit_metaclass(db);
@@ -3786,8 +3791,8 @@ pub(crate) mod tests {
37863791
let elements = tys.into_iter().map(|ty| ty.into_type(db));
37873792
TupleType::from_elements(db, elements)
37883793
}
3789-
Ty::SubclassOfAny => SubclassOfType::from(db, ClassBase::Any),
3790-
Ty::SubclassOfUnknown => SubclassOfType::from(db, ClassBase::Unknown),
3794+
Ty::SubclassOfAny => SubclassOfType::subclass_of_any(),
3795+
Ty::SubclassOfUnknown => SubclassOfType::subclass_of_unknown(),
37913796
Ty::SubclassOfBuiltinClass(s) => SubclassOfType::from(
37923797
db,
37933798
builtins_symbol(db, s)

crates/red_knot_python_semantic/src/types/infer.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ use crate::semantic_index::semantic_index;
4949
use crate::semantic_index::symbol::{NodeWithScopeKind, NodeWithScopeRef, ScopeId};
5050
use crate::semantic_index::SemanticIndex;
5151
use crate::stdlib::builtins_module_scope;
52-
use crate::types::class_base::ClassBase;
5352
use crate::types::diagnostic::{
5453
report_invalid_assignment, report_unresolved_module, TypeCheckDiagnostics, CALL_NON_CALLABLE,
5554
CALL_POSSIBLY_UNBOUND_METHOD, CONFLICTING_DECLARATIONS, CONFLICTING_METACLASS,
@@ -4895,7 +4894,7 @@ impl<'db> TypeInferenceBuilder<'db> {
48954894
SubclassOfType::from(self.db(), class)
48964895
}
48974896
Type::KnownInstance(KnownInstanceType::Any) => {
4898-
SubclassOfType::from(self.db(), ClassBase::Any)
4897+
SubclassOfType::subclass_of_any()
48994898
}
49004899
_ => todo_type!("unsupported type[X] special form"),
49014900
}

crates/red_knot_python_semantic/src/types/subclass_of.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,17 @@ pub struct SubclassOfType<'db> {
88
}
99

1010
impl<'db> SubclassOfType<'db> {
11+
/// Construct a new [`Type`] instance representing a given class object (or a given dynamic type)
12+
/// and all possible subclasses of that class object/dynamic type.
13+
///
14+
/// This method does not always return a [`Type::SubclassOf`] variant.
15+
/// If the class object is known to be a final class,
16+
/// this method will return a [`Type::ClassLiteral`] variant; this is a more precise type.
17+
/// If the class object is `builtins.object`, `Type::Instance(<builtins.type>)` will be returned;
18+
/// this is no more precise, but it is exactly equivalent to `type[object]`.
19+
///
20+
/// The eager normalization here means that we do not need to worry elsewhere about distinguishing
21+
/// between `@final` classes and other classes when dealing with [`Type::SubclassOf`] variants.
1122
pub fn from(db: &'db dyn Db, subclass_of: impl Into<ClassBase<'db>>) -> Type<'db> {
1223
let subclass_of = subclass_of.into();
1324
match subclass_of {
@@ -26,6 +37,21 @@ impl<'db> SubclassOfType<'db> {
2637
}
2738
}
2839

40+
/// Return a [`Type`] instance representing the type `type[Unknown]`.
41+
pub const fn subclass_of_unknown() -> Type<'db> {
42+
Type::SubclassOf(SubclassOfType {
43+
subclass_of: ClassBase::Unknown,
44+
})
45+
}
46+
47+
/// Return a [`Type`] instance representing the type `type[Any]`.
48+
pub const fn subclass_of_any() -> Type<'db> {
49+
Type::SubclassOf(SubclassOfType {
50+
subclass_of: ClassBase::Any,
51+
})
52+
}
53+
54+
/// Return the inner `ClassBase` value wrapped by this `SubclassOfType`.
2955
pub const fn subclass_of(self) -> ClassBase<'db> {
3056
self.subclass_of
3157
}
@@ -44,6 +70,10 @@ impl<'db> SubclassOfType<'db> {
4470
Type::from(self.subclass_of).member(db, name)
4571
}
4672

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

0 commit comments

Comments
 (0)