Skip to content

Commit eb8df86

Browse files
authored
Merge pull request #58522 from ahoppen/pr/no-circular-supertypes
[CodeCompletion] Avoid creating circles in the USRBasedType supertype hierarchy
2 parents 1b1c507 + b4fca1a commit eb8df86

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
@@ -204,10 +204,38 @@ const USRBasedType *USRBasedType::fromType(Type Ty, USRBasedTypeArena &Arena) {
204204
Conformance->getProtocol()->getDeclaredInterfaceType(), Arena));
205205
}
206206
}
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);
208236
while (Superclass) {
209237
Supertypes.push_back(USRBasedType::fromType(Superclass, Arena));
210-
Superclass = Superclass->getSuperclass();
238+
Superclass = getSuperclass(Superclass);
211239
}
212240

213241
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)