Skip to content

Commit 616edf7

Browse files
committed
[cxx-interop] Fix circular reference errors during conformance synthesis
These errors were sometimes produced when synthesizing conformance to `UnsafeCxxInputIterator`: ``` <unknown>:0: error: circular reference <unknown>:0: note: through reference here <unknown>:0: note: through reference here ``` This happened because `NominalTypeDecl::lookupDirect` attempts to deserialize Swift extensions of the type, which might belong to the module which has a dependency to the module that is currently being imported, which leads to deserialization errors. This change makes sure we don't call `NominalTypeDecl::lookupDirect` when synthesizing conformances for C++ types.
1 parent f10e66e commit 616edf7

File tree

2 files changed

+34
-10
lines changed

2 files changed

+34
-10
lines changed

lib/ClangImporter/ClangDerivedConformances.cpp

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,36 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
#include "ClangDerivedConformances.h"
14-
#include "swift/AST/NameLookup.h"
1514
#include "swift/AST/ParameterList.h"
1615
#include "swift/AST/PrettyStackTrace.h"
16+
#include "swift/AST/ProtocolConformance.h"
17+
#include "swift/ClangImporter/ClangImporterRequests.h"
1718

1819
using namespace swift;
1920

21+
/// Alternative to `NominalTypeDecl::lookupDirect`.
22+
/// This function does not attempt to load extensions of the nominal decl.
23+
static TinyPtrVector<ValueDecl *>
24+
lookupDirectWithoutExtensions(NominalTypeDecl *decl, Identifier id) {
25+
// First see if there is a Clang decl with the given name.
26+
TinyPtrVector<ValueDecl *> result = evaluateOrDefault(
27+
decl->getASTContext().evaluator, ClangRecordMemberLookup({decl, id}), {});
28+
29+
// Check if there are any synthesized Swift members that match the name.
30+
for (auto member : decl->getMembers()) {
31+
if (auto namedMember = dyn_cast<ValueDecl>(member)) {
32+
if (namedMember->hasName() && !namedMember->getName().isSpecial() &&
33+
namedMember->getName().getBaseIdentifier().is(id.str()) &&
34+
// Make sure we don't add duplicate entries, as that would wrongly
35+
// imply that lookup is ambiguous.
36+
!llvm::is_contained(result, namedMember)) {
37+
result.push_back(namedMember);
38+
}
39+
}
40+
}
41+
return result;
42+
}
43+
2044
static clang::TypeDecl *
2145
getIteratorCategoryDecl(const clang::CXXRecordDecl *clangDecl) {
2246
clang::IdentifierInfo *iteratorCategoryDeclName =
@@ -55,7 +79,7 @@ static ValueDecl *getEqualEqualOperator(NominalTypeDecl *decl) {
5579
};
5680

5781
// First look for `func ==` declared as a member.
58-
auto memberResults = decl->lookupDirect(id);
82+
auto memberResults = lookupDirectWithoutExtensions(decl, id);
5983
for (const auto &member : memberResults) {
6084
if (isValid(member))
6185
return member;
@@ -131,7 +155,7 @@ void swift::conformToCxxIteratorIfNeeded(
131155

132156
// Check if present: `var pointee: Pointee { get }`
133157
auto pointeeId = ctx.getIdentifier("pointee");
134-
auto pointees = decl->lookupDirect(pointeeId);
158+
auto pointees = lookupDirectWithoutExtensions(decl, pointeeId);
135159
if (pointees.size() != 1)
136160
return;
137161
auto pointee = dyn_cast<VarDecl>(pointees.front());
@@ -140,7 +164,7 @@ void swift::conformToCxxIteratorIfNeeded(
140164

141165
// Check if present: `func successor() -> Self`
142166
auto successorId = ctx.getIdentifier("successor");
143-
auto successors = decl->lookupDirect(successorId);
167+
auto successors = lookupDirectWithoutExtensions(decl, successorId);
144168
if (successors.size() != 1)
145169
return;
146170
auto successor = dyn_cast<FuncDecl>(successors.front());

test/Interop/Cxx/stdlib/overlay/custom-iterator-module-interface.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
// CHECK: struct ConstIterator : UnsafeCxxInputIterator {
66
// CHECK: var pointee: Int32 { get }
77
// CHECK: func successor() -> ConstIterator
8-
// CHECK: typealias Pointee = Int32
98
// CHECK: static func == (lhs: ConstIterator, other: ConstIterator) -> Bool
9+
// CHECK: typealias Pointee = Int32
1010
// CHECK: }
1111

1212
// CHECK: struct ConstIteratorOutOfLineEq : UnsafeCxxInputIterator {
@@ -18,36 +18,36 @@
1818
// CHECK: struct MinimalIterator : UnsafeCxxInputIterator {
1919
// CHECK: var pointee: Int32 { get }
2020
// CHECK: func successor() -> MinimalIterator
21-
// CHECK: typealias Pointee = Int32
2221
// CHECK: static func == (lhs: MinimalIterator, other: MinimalIterator) -> Bool
22+
// CHECK: typealias Pointee = Int32
2323
// CHECK: }
2424

2525
// CHECK: struct ForwardIterator : UnsafeCxxInputIterator {
2626
// CHECK: var pointee: Int32 { get }
2727
// CHECK: func successor() -> ForwardIterator
28-
// CHECK: typealias Pointee = Int32
2928
// CHECK: static func == (lhs: ForwardIterator, other: ForwardIterator) -> Bool
29+
// CHECK: typealias Pointee = Int32
3030
// CHECK: }
3131

3232
// CHECK: struct HasCustomIteratorTag : UnsafeCxxInputIterator {
3333
// CHECK: var pointee: Int32 { get }
3434
// CHECK: func successor() -> HasCustomIteratorTag
35-
// CHECK: typealias Pointee = Int32
3635
// CHECK: static func == (lhs: HasCustomIteratorTag, other: HasCustomIteratorTag) -> Bool
36+
// CHECK: typealias Pointee = Int32
3737
// CHECK: }
3838

3939
// CHECK: struct HasCustomIteratorTagInline : UnsafeCxxInputIterator {
4040
// CHECK: var pointee: Int32 { get }
4141
// CHECK: func successor() -> HasCustomIteratorTagInline
42-
// CHECK: typealias Pointee = Int32
4342
// CHECK: static func == (lhs: HasCustomIteratorTagInline, other: HasCustomIteratorTagInline) -> Bool
43+
// CHECK: typealias Pointee = Int32
4444
// CHECK: }
4545

4646
// CHECK: struct HasTypedefIteratorTag : UnsafeCxxInputIterator {
4747
// CHECK: var pointee: Int32 { get }
4848
// CHECK: func successor() -> HasTypedefIteratorTag
49-
// CHECK: typealias Pointee = Int32
5049
// CHECK: static func == (lhs: HasTypedefIteratorTag, other: HasTypedefIteratorTag) -> Bool
50+
// CHECK: typealias Pointee = Int32
5151
// CHECK: }
5252

5353
// CHECK-NOT: struct HasNoIteratorCategory : UnsafeCxxInputIterator

0 commit comments

Comments
 (0)