Skip to content

Commit 309543b

Browse files
authored
Merge pull request #58636 from ahoppen/pr-5.7/no-circular-supertypes
[5.7][CodeCompletion] Avoid creating circles in the USRBasedType supertype hierarchy
2 parents a10318d + 6dad5a0 commit 309543b

File tree

2 files changed

+53
-2
lines changed

2 files changed

+53
-2
lines changed

lib/IDE/CodeCompletionResultType.cpp

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,10 +203,38 @@ const USRBasedType *USRBasedType::fromType(Type Ty, USRBasedTypeArena &Arena) {
203203
Conformance->getProtocol()->getDeclaredInterfaceType(), Arena));
204204
}
205205
}
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);
207235
while (Superclass) {
208236
Supertypes.push_back(USRBasedType::fromType(Superclass, Arena));
209-
Superclass = Superclass->getSuperclass();
237+
Superclass = getSuperclass(Superclass);
210238
}
211239

212240
assert(llvm::all_of(Supertypes, [&USR](const USRBasedType *Ty) {
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %empty-directory(%t/ImportPath)
3+
// RUN: %{python} %utils/split_file.py -o %t %s
4+
5+
// RUN: %target-swift-frontend -disable-availability-checking -emit-module %t/Lib.swift -o %t/ImportPath/Lib.swiftmodule -emit-module-interface-path %t/ImportPath/Lib.swiftinterface
6+
7+
// BEGIN Lib.swift
8+
9+
// Proto and Class have a circular supertype relationship.
10+
11+
public protocol Proto : Class {}
12+
public class Class : Proto {}
13+
14+
// BEGIN test.swift
15+
16+
import Lib
17+
18+
// RUN: %empty-directory(%t/completion-cache)
19+
// RUN: %target-swift-ide-test -code-completion -source-filename %t/test.swift -code-completion-token COMPLETE -I %t/ImportPath
20+
21+
func test() -> Proto {
22+
return #^COMPLETE^#
23+
}

0 commit comments

Comments
 (0)