@@ -204,10 +204,38 @@ const USRBasedType *USRBasedType::fromType(Type Ty, USRBasedTypeArena &Arena) {
204
204
Conformance->getProtocol ()->getDeclaredInterfaceType (), Arena));
205
205
}
206
206
}
207
- Type Superclass = Ty->getSuperclass ();
207
+
208
+ // You would think that superclass + conformances form a DAG. You are wrong!
209
+ // We can achieve a circular supertype hierarcy with
210
+ //
211
+ // protocol Proto : Class {}
212
+ // class Class : Proto {}
213
+ //
214
+ // USRBasedType is not set up for this. Serialization of code completion
215
+ // results from global modules can't handle cycles in the supertype hierarchy
216
+ // because it writes the DAG leaf to root(s) and needs to know the type
217
+ // offsets. To get consistent results independent of where we start
218
+ // constructing USRBasedTypes, ignore superclasses of protocols. If we kept
219
+ // track of already visited types, we would get different results depending on
220
+ // whether we start constructing the USRBasedType hierarchy from Proto or
221
+ // Class.
222
+ // Ignoring superclasses of protocols is safe to do because USRBasedType is an
223
+ // under-approximation anyway.
224
+
225
+ // / If `Ty` is a class type and has a superclass, return that. In all other
226
+ // / cases, return null.
227
+ auto getSuperclass = [](Type Ty) -> Type {
228
+ if (isa_and_nonnull<ClassDecl>(Ty->getAnyNominal ())) {
229
+ return Ty->getSuperclass ();
230
+ } else {
231
+ return Type ();
232
+ }
233
+ };
234
+
235
+ Type Superclass = getSuperclass (Ty);
208
236
while (Superclass) {
209
237
Supertypes.push_back (USRBasedType::fromType (Superclass, Arena));
210
- Superclass = Superclass-> getSuperclass ();
238
+ Superclass = getSuperclass (Superclass );
211
239
}
212
240
213
241
assert (llvm::all_of (Supertypes, [&USR](const USRBasedType *Ty) {
0 commit comments