@@ -88,27 +88,37 @@ USRBasedTypeContext::USRBasedTypeContext(const ExpectedTypeContext *TypeContext,
8888
8989TypeRelation
9090USRBasedTypeContext::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-
157135const 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
338316TypeRelation 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