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