@@ -88,27 +88,37 @@ USRBasedTypeContext::USRBasedTypeContext(const ExpectedTypeContext *TypeContext,
88
88
89
89
TypeRelation
90
90
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
+ }
103
105
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
+ }
109
112
}
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;
112
122
}
113
123
114
124
// MARK: - USRBasedTypeArena
@@ -122,38 +132,6 @@ const USRBasedType *USRBasedTypeArena::getVoidType() const { return VoidType; }
122
132
123
133
// MARK: - USRBasedType
124
134
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
-
157
135
const USRBasedType *USRBasedType::null (USRBasedTypeArena &Arena) {
158
136
return USRBasedType::fromUSR (/* USR=*/ " " , /* Supertypes=*/ {}, {}, Arena);
159
137
}
@@ -337,8 +315,37 @@ const USRBasedType *USRBasedType::fromType(Type Ty, USRBasedTypeArena &Arena) {
337
315
338
316
TypeRelation USRBasedType::typeRelation (const USRBasedType *ResultType,
339
317
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;
342
349
}
343
350
344
351
// MARK: - USRBasedTypeContext
0 commit comments