Skip to content

Commit f3a56c3

Browse files
committed
many cleanups
1 parent d96a3bd commit f3a56c3

File tree

3 files changed

+49
-25
lines changed

3 files changed

+49
-25
lines changed

crates/red_knot_python_semantic/src/types.rs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -899,6 +899,7 @@ impl<'db> Type<'db> {
899899
/// as these are irrelevant to whether a callable type `X` is equivalent to a callable type `Y`.
900900
/// - Strips the types of default values from parameters in `Callable` types: only whether a parameter
901901
/// *has* or *does not have* a default value is relevant to whether two `Callable` types are equivalent.
902+
/// - Converts class-based protocols into synthesized protocols
902903
#[must_use]
903904
pub fn normalized(self, db: &'db dyn Db) -> Self {
904905
match self {
@@ -1604,13 +1605,6 @@ impl<'db> Type<'db> {
16041605
}
16051606
}
16061607

1607-
fn satisfies_protocol(self, db: &'db dyn Db, protocol: ProtocolInstanceType<'db>) -> bool {
1608-
protocol
1609-
.protocol_members(db)
1610-
.iter()
1611-
.all(|member| !self.member(db, member).symbol.is_unbound())
1612-
}
1613-
16141608
/// Return true if this type and `other` have no common elements.
16151609
///
16161610
/// Note: This function aims to have no false positives, but might return

crates/red_knot_python_semantic/src/types/class.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -144,10 +144,6 @@ impl<'db> ClassType<'db> {
144144
}
145145
}
146146

147-
pub(super) fn is_protocol(self, db: &'db dyn Db) -> bool {
148-
self.class_literal(db).0.is_protocol(db)
149-
}
150-
151147
pub(crate) fn name(self, db: &'db dyn Db) -> &'db ast::name::Name {
152148
let (class_literal, _) = self.class_literal(db);
153149
class_literal.name(db)

crates/red_knot_python_semantic/src/types/instance.rs

Lines changed: 48 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crate::{Db, FxOrderSet};
77

88
impl<'db> Type<'db> {
99
pub(crate) fn instance(db: &'db dyn Db, class: ClassType<'db>) -> Self {
10-
if class.is_protocol(db) {
10+
if class.class_literal(db).0.is_protocol(db) {
1111
Self::ProtocolInstance(ProtocolInstanceType(Protocol::FromClass(class)))
1212
} else {
1313
Self::NominalInstance(NominalInstanceType { class })
@@ -20,6 +20,21 @@ impl<'db> Type<'db> {
2020
_ => None,
2121
}
2222
}
23+
24+
/// Return `true` if `self` conforms to the interface described by `protocol`.
25+
pub(super) fn satisfies_protocol(
26+
self,
27+
db: &'db dyn Db,
28+
protocol: ProtocolInstanceType<'db>,
29+
) -> bool {
30+
// TODO: this should consider the types of the protocol members
31+
// as well as whether each member *exists* on `self`.
32+
protocol
33+
.0
34+
.protocol_members(db)
35+
.iter()
36+
.all(|member| !self.member(db, member).symbol.is_unbound())
37+
}
2338
}
2439

2540
/// A type representing the set of runtime objects which are instances of a certain nominal class.
@@ -99,6 +114,8 @@ impl<'db> From<NominalInstanceType<'db>> for Type<'db> {
99114
}
100115
}
101116

117+
/// A `ProtocolInstanceType` represents the set of all possible runtime objects
118+
/// that conform to the interface described by a certain protocol.
102119
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord, salsa::Update)]
103120
pub struct ProtocolInstanceType<'db>(
104121
// Keep the inner field here private,
@@ -108,14 +125,11 @@ pub struct ProtocolInstanceType<'db>(
108125
);
109126

110127
impl<'db> ProtocolInstanceType<'db> {
111-
pub(super) fn protocol_members(self, db: &'db dyn Db) -> &'db FxOrderSet<Name> {
112-
self.0.protocol_members(db)
113-
}
114-
115128
pub(super) fn inner(self) -> Protocol<'db> {
116129
self.0
117130
}
118131

132+
/// Return the meta-type of this protocol-instance type.
119133
pub(super) fn to_meta_type(self, db: &'db dyn Db) -> Type<'db> {
120134
match self.0 {
121135
Protocol::FromClass(class) => SubclassOfType::from(db, class),
@@ -137,53 +151,68 @@ impl<'db> ProtocolInstanceType<'db> {
137151
}
138152
}
139153

154+
/// Return a "normalized" version of this `Protocol` type.
155+
///
156+
/// See [`Type::normalized`] for more details.
140157
pub(super) fn normalized(self, db: &'db dyn Db) -> Type<'db> {
141158
let object = KnownClass::Object.to_instance(db);
142159
if object.satisfies_protocol(db, self) {
143160
return object;
144161
}
145162
match self.0 {
146163
Protocol::FromClass(_) => Type::ProtocolInstance(Self(Protocol::Synthesized(
147-
SynthesizedProtocolType::new(db, self.protocol_members(db)),
164+
SynthesizedProtocolType::new(db, self.0.protocol_members(db)),
148165
))),
149166
Protocol::Synthesized(_) => Type::ProtocolInstance(self),
150167
}
151168
}
152169

153-
/// TODO: should iterate over the types of the members
154-
/// and check if any of them contain `Todo` types
170+
/// TODO: this should return `true` if any of the members of this protocol type contain any `Todo` types.
155171
#[expect(clippy::unused_self)]
156172
pub(super) fn contains_todo(self) -> bool {
157173
false
158174
}
159175

176+
/// Return `true` if this protocol type is fully static.
177+
///
160178
/// TODO: should not be considered fully static if any members do not have fully static types
161179
#[expect(clippy::unused_self)]
162180
pub(super) fn is_fully_static(self) -> bool {
163181
true
164182
}
165183

184+
/// Return `true` if this protocol type is a subtype of the protocol `other`.
185+
///
166186
/// TODO: consider the types of the members as well as their existence
167187
pub(super) fn is_subtype_of(self, db: &'db dyn Db, other: Self) -> bool {
168-
self.protocol_members(db)
169-
.is_superset(other.protocol_members(db))
188+
self.0
189+
.protocol_members(db)
190+
.is_superset(other.0.protocol_members(db))
170191
}
171192

193+
/// Return `true` if this protocol type is assignable to the protocol `other`.
194+
///
172195
/// TODO: consider the types of the members as well as their existence
173196
pub(super) fn is_assignable_to(self, db: &'db dyn Db, other: Self) -> bool {
174197
self.is_subtype_of(db, other)
175198
}
176199

200+
/// Return `true` if this protocol type is equivalent to the protocol `other`.
201+
///
177202
/// TODO: consider the types of the members as well as their existence
178203
pub(super) fn is_equivalent_to(self, db: &'db dyn Db, other: Self) -> bool {
179204
self.normalized(db) == other.normalized(db)
180205
}
181206

207+
/// Return `true` if this protocol type is gradually equivalent to the protocol `other`.
208+
///
182209
/// TODO: consider the types of the members as well as their existence
183210
pub(super) fn is_gradual_equivalent_to(self, db: &'db dyn Db, other: Self) -> bool {
184211
self.is_equivalent_to(db, other)
185212
}
186213

214+
/// Return `true` if this protocol type is disjoint from the protocol `other`.
215+
///
187216
/// TODO: a protocol `X` is disjoint from a protocol `Y` if `X` and `Y`
188217
/// have a member with the same name but disjoint types
189218
#[expect(clippy::unused_self)]
@@ -192,9 +221,8 @@ impl<'db> ProtocolInstanceType<'db> {
192221
}
193222
}
194223

195-
/// Private inner enum to represent the two kinds of protocol types.
196-
/// This is not exposed publicly, so that the only way of constructing `Protocol` instances
197-
/// is through the [`Type::instance`] constructor function.
224+
/// An enumeration of the two kinds of protocol types: those that originate from a class
225+
/// definition in source code, and those that are synthesized from a set of members.
198226
#[derive(
199227
Copy, Clone, Debug, Eq, PartialEq, Hash, salsa::Update, salsa::Supertype, PartialOrd, Ord,
200228
)]
@@ -203,8 +231,8 @@ pub(super) enum Protocol<'db> {
203231
Synthesized(SynthesizedProtocolType<'db>),
204232
}
205233

206-
#[salsa::tracked]
207234
impl<'db> Protocol<'db> {
235+
/// Return the members of this protocol type
208236
fn protocol_members(self, db: &'db dyn Db) -> &'db FxOrderSet<Name> {
209237
match self {
210238
Self::FromClass(class) => class
@@ -218,6 +246,12 @@ impl<'db> Protocol<'db> {
218246
}
219247
}
220248

249+
/// A "synthesized" protocol type that is dissociated from a class definition in source code.
250+
///
251+
/// Two synthesized protocol types with the same members will share the same Salsa ID,
252+
/// making them easy to compare for equivalence. A synthesized protocol type is therefore
253+
/// returned by [`ProtocolInstanceType::normalized`] so that two protocols with the same members
254+
/// will be understood as equivalent even in the context of differently ordered unions or intersections.
221255
#[salsa::interned(debug)]
222256
pub(super) struct SynthesizedProtocolType<'db> {
223257
#[return_ref]

0 commit comments

Comments
 (0)