Skip to content

Commit 11e8df0

Browse files
authored
Merge pull request #77322 from hamishknight/complete-cache
Add cache for `USRBasedTypeContext::typeRelation`
2 parents 501f2be + 9ca1157 commit 11e8df0

File tree

2 files changed

+64
-59
lines changed

2 files changed

+64
-59
lines changed

include/swift/IDE/CodeCompletionResultType.h

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,10 @@ class USRBasedTypeContext {
207207
/// allocated.
208208
const USRBasedTypeArena &Arena;
209209

210+
/// A cached set of type relations for this given type context.
211+
mutable llvm::DenseMap<const USRBasedType *, CodeCompletionResultTypeRelation>
212+
CachedTypeRelations;
213+
210214
SmallVector<ContextualType, 4> ContextualTypes;
211215

212216
/// See \c ExpectedTypeContext::ExpectedAttributeKinds.
@@ -251,12 +255,6 @@ class USRBasedType {
251255
: USR(USR), Supertypes(Supertypes),
252256
CustomAttributeKinds(CustomAttributeKinds) {}
253257

254-
/// Implementation of \c typeRelation. \p VisistedTypes keeps track which
255-
/// types have already been visited.
256-
CodeCompletionResultTypeRelation
257-
typeRelationImpl(const USRBasedType *ResultType, const USRBasedType *VoidType,
258-
SmallPtrSetImpl<const USRBasedType *> &VisitedTypes) const;
259-
260258
public:
261259
/// A null \c USRBasedType that's represented by an empty USR and has no
262260
/// supertypes.

lib/IDE/CodeCompletionResultType.cpp

Lines changed: 60 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -88,27 +88,37 @@ USRBasedTypeContext::USRBasedTypeContext(const ExpectedTypeContext *TypeContext,
8888

8989
TypeRelation
9090
USRBasedTypeContext::typeRelation(const USRBasedType *ResultType) const {
91-
if (ExpectedCustomAttributeKinds) {
92-
return ResultType->getCustomAttributeKinds() & ExpectedCustomAttributeKinds
93-
? TypeRelation::Convertible
94-
: TypeRelation::Unrelated;
95-
}
96-
const USRBasedType *VoidType = Arena.getVoidType();
97-
if (ResultType == VoidType) {
98-
// Void is not convertible to anything and we don't report Void <-> Void
99-
// identical matches (see USRBasedType::typeRelation). So we don't have to
100-
// check anything if the result returns Void.
101-
return TypeRelation::Unknown;
102-
}
91+
auto compute = [&]() -> TypeRelation {
92+
if (ExpectedCustomAttributeKinds) {
93+
return ResultType->getCustomAttributeKinds() &
94+
ExpectedCustomAttributeKinds
95+
? TypeRelation::Convertible
96+
: TypeRelation::Unrelated;
97+
}
98+
const USRBasedType *VoidType = Arena.getVoidType();
99+
if (ResultType == VoidType) {
100+
// Void is not convertible to anything and we don't report Void <-> Void
101+
// identical matches (see USRBasedType::typeRelation). So we don't have to
102+
// check anything if the result returns Void.
103+
return TypeRelation::Unknown;
104+
}
103105

104-
TypeRelation Res = TypeRelation::Unknown;
105-
for (auto &ContextualType : ContextualTypes) {
106-
Res = std::max(Res, ContextualType.typeRelation(ResultType, VoidType));
107-
if (Res == TypeRelation::MAX_VALUE) {
108-
return Res; // We can't improve further
106+
TypeRelation Res = TypeRelation::Unknown;
107+
for (auto &ContextualType : ContextualTypes) {
108+
Res = std::max(Res, ContextualType.typeRelation(ResultType, VoidType));
109+
if (Res == TypeRelation::MAX_VALUE) {
110+
return Res; // We can't improve further
111+
}
109112
}
110-
}
111-
return Res;
113+
return Res;
114+
};
115+
auto iter = CachedTypeRelations.find(ResultType);
116+
if (iter != CachedTypeRelations.end())
117+
return iter->second;
118+
119+
auto relation = compute();
120+
CachedTypeRelations.insert({ResultType, relation});
121+
return relation;
112122
}
113123

114124
// MARK: - USRBasedTypeArena
@@ -122,38 +132,6 @@ const USRBasedType *USRBasedTypeArena::getVoidType() const { return VoidType; }
122132

123133
// MARK: - USRBasedType
124134

125-
TypeRelation USRBasedType::typeRelationImpl(
126-
const USRBasedType *ResultType, const USRBasedType *VoidType,
127-
SmallPtrSetImpl<const USRBasedType *> &VisitedTypes) const {
128-
129-
// `this` is the contextual type.
130-
if (this == VoidType) {
131-
// We don't report Void <-> Void matches because that would boost
132-
// methods returning Void in e.g.
133-
// func foo() { #^COMPLETE^# }
134-
// because #^COMPLETE^# is implicitly returned. But that's not very
135-
// helpful.
136-
return TypeRelation::Unknown;
137-
}
138-
if (ResultType == this) {
139-
return TypeRelation::Convertible;
140-
}
141-
for (const USRBasedType *Supertype : ResultType->getSupertypes()) {
142-
if (!VisitedTypes.insert(Supertype).second) {
143-
// Already visited this type.
144-
continue;
145-
}
146-
if (this->typeRelation(Supertype, VoidType) >= TypeRelation::Convertible) {
147-
return TypeRelation::Convertible;
148-
}
149-
}
150-
// TypeRelation computation based on USRs is an under-approximation because we
151-
// don't take into account generic conversions or retroactive conformance of
152-
// library types. Hence, we can't know for sure that ResultType is not
153-
// convertible to `this` type and thus can't return Unrelated or Invalid here.
154-
return TypeRelation::Unknown;
155-
}
156-
157135
const USRBasedType *USRBasedType::null(USRBasedTypeArena &Arena) {
158136
return USRBasedType::fromUSR(/*USR=*/"", /*Supertypes=*/{}, {}, Arena);
159137
}
@@ -337,8 +315,37 @@ const USRBasedType *USRBasedType::fromType(Type Ty, USRBasedTypeArena &Arena) {
337315

338316
TypeRelation USRBasedType::typeRelation(const USRBasedType *ResultType,
339317
const USRBasedType *VoidType) const {
340-
SmallPtrSet<const USRBasedType *, 4> VisitedTypes;
341-
return this->typeRelationImpl(ResultType, VoidType, VisitedTypes);
318+
// `this` is the contextual type.
319+
if (this == VoidType) {
320+
// We don't report Void <-> Void matches because that would boost
321+
// methods returning Void in e.g.
322+
// func foo() { #^COMPLETE^# }
323+
// because #^COMPLETE^# is implicitly returned. But that's not very
324+
// helpful.
325+
return TypeRelation::Unknown;
326+
}
327+
328+
SmallPtrSet<const USRBasedType *, 16> VisitedTypes;
329+
SmallVector<const USRBasedType *, 16> Worklist;
330+
Worklist.push_back(ResultType);
331+
while (!Worklist.empty()) {
332+
auto *CurrentType = Worklist.pop_back_val();
333+
if (CurrentType == this)
334+
return TypeRelation::Convertible;
335+
336+
for (const USRBasedType *Supertype : CurrentType->getSupertypes()) {
337+
if (!VisitedTypes.insert(Supertype).second) {
338+
// Already visited this type.
339+
continue;
340+
}
341+
Worklist.push_back(Supertype);
342+
}
343+
}
344+
// TypeRelation computation based on USRs is an under-approximation because we
345+
// don't take into account generic conversions or retroactive conformance of
346+
// library types. Hence, we can't know for sure that ResultType is not
347+
// convertible to `this` type and thus can't return Unrelated or Invalid here.
348+
return TypeRelation::Unknown;
342349
}
343350

344351
// MARK: - USRBasedTypeContext

0 commit comments

Comments
 (0)