Skip to content

Commit 745d92d

Browse files
committed
[cxx-interop] Allow instantiated operator methods to serve as protocol conformance witnesses
If a templated C++ class declares an operator as a member function, and is instantiated using a typedef or a using-decl on the C++ side, it previously could not be conformed to a Swift protocol that requires the operator function despite matching signatures. This was due to a Swift name lookup issue: operators, unlike regular member functions, are found by doing an unqualified lookup. Since C++ class template specializations and their members are not added to `SwiftLookupTable`, when doing qualified lookup members are searched by looking at all of the members of the specialization and choosing the ones with matching names. With unqualified lookup, we cannot rely on knowing the right specialization and need to search for all the operators in a given module. This change adds synthesized operator thunks to `SwiftLookupTable` to make them discoverable by unqualified lookup.
1 parent 0620226 commit 745d92d

File tree

5 files changed

+50
-9
lines changed

5 files changed

+50
-9
lines changed

lib/ClangImporter/ClangImporter.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4021,6 +4021,30 @@ SwiftLookupTable *ClangImporter::Implementation::findLookupTable(
40214021
return known->second.get();
40224022
}
40234023

4024+
SwiftLookupTable *
4025+
ClangImporter::Implementation::findLookupTable(const clang::Decl *decl) {
4026+
// Contents of a C++ namespace are added to the __ObjC module.
4027+
bool isWithinNamespace = false;
4028+
auto declContext = decl->getDeclContext();
4029+
while (!declContext->isTranslationUnit()) {
4030+
if (declContext->isNamespace()) {
4031+
isWithinNamespace = true;
4032+
break;
4033+
}
4034+
declContext = declContext->getParent();
4035+
}
4036+
4037+
clang::Module *owningModule = nullptr;
4038+
if (!isWithinNamespace) {
4039+
// Members of class template specializations don't have an owning module.
4040+
if (auto spec = dyn_cast<clang::ClassTemplateSpecializationDecl>(decl))
4041+
owningModule = spec->getSpecializedTemplate()->getOwningModule();
4042+
else
4043+
owningModule = decl->getOwningModule();
4044+
}
4045+
return findLookupTable(owningModule);
4046+
}
4047+
40244048
bool ClangImporter::Implementation::forEachLookupTable(
40254049
llvm::function_ref<bool(SwiftLookupTable &table)> fn) {
40264050
// Visit the bridging header's lookup table.

lib/ClangImporter/ImportDecl.cpp

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2118,15 +2118,7 @@ namespace {
21182118
if (friendDecl->getFriendDecl()) {
21192119
m = friendDecl->getFriendDecl();
21202120

2121-
// Find the owning module of the class template. Members of class
2122-
// template specializations don't have an owning module.
2123-
clang::Module *owningModule = nullptr;
2124-
if (auto spec = dyn_cast<clang::ClassTemplateSpecializationDecl>(decl))
2125-
owningModule = spec->getSpecializedTemplate()->getOwningModule();
2126-
else
2127-
owningModule = decl->getOwningModule();
2128-
2129-
auto lookupTable = Impl.findLookupTable(owningModule);
2121+
auto lookupTable = Impl.findLookupTable(decl);
21302122
addEntryToLookupTable(*lookupTable, friendDecl->getFriendDecl(),
21312123
Impl.getNameImporter());
21322124
}
@@ -2243,6 +2235,9 @@ namespace {
22432235

22442236
// Make sure the synthesized decl can be found by lookupDirect.
22452237
result->addMemberToLookupTable(opFuncDecl);
2238+
2239+
addEntryToLookupTable(*Impl.findLookupTable(decl), cxxMethod,
2240+
Impl.getNameImporter());
22462241
}
22472242
}
22482243
methods.push_back(MD);

lib/ClangImporter/ImporterImpl.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1644,6 +1644,9 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
16441644
/// about the directly-parsed headers.
16451645
SwiftLookupTable *findLookupTable(const clang::Module *clangModule);
16461646

1647+
/// Find the lookup table that should contain the given Clang declaration.
1648+
SwiftLookupTable *findLookupTable(const clang::Decl *decl);
1649+
16471650
/// Visit each of the lookup tables in some deterministic order.
16481651
///
16491652
/// \param fn Invoke the given visitor for each table. If the

test/Interop/Cxx/class/Inputs/protocol-conformance.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,16 @@ struct HasOperatorEqualEqual {
5555
}
5656
};
5757

58+
template <typename T>
59+
struct HasOperatorPlusEqual {
60+
T value;
61+
62+
HasOperatorPlusEqual &operator+=(int x) {
63+
value += x;
64+
return *this;
65+
}
66+
};
67+
68+
using HasOperatorPlusEqualInt = HasOperatorPlusEqual<int>;
69+
5870
#endif // TEST_INTEROP_CXX_CLASS_INPUTS_PROTOCOL_CONFORMANCE_H

test/Interop/Cxx/class/protocol-conformance-typechecker.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,10 @@ protocol Invertable {
3535
extension HasOperatorExclaim: Invertable {}
3636

3737
extension HasOperatorEqualEqual: Equatable {}
38+
39+
40+
protocol HasOperatorPlusEqualProtocol {
41+
static func +=(lhs: inout Self, x: Int32)
42+
}
43+
44+
extension HasOperatorPlusEqualInt : HasOperatorPlusEqualProtocol {}

0 commit comments

Comments
 (0)