Skip to content

Commit 38dac1a

Browse files
authored
Merge pull request #62557 from apple/egorzhdan/cxx-lookup-op-template
[cxx-interop] Fix lookup failure for operators in templated classes
2 parents 806707d + bbf8f0d commit 38dac1a

File tree

7 files changed

+103
-41
lines changed

7 files changed

+103
-41
lines changed

include/swift/ClangImporter/ClangImporter.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,8 @@ class ClangImporter final : public ClangModuleLoader {
275275
ConcreteDeclRef getCXXFunctionTemplateSpecialization(
276276
SubstitutionMap subst, ValueDecl *decl) override;
277277

278+
FuncDecl *getCXXSynthesizedOperatorFunc(FuncDecl *decl);
279+
278280
/// Just like Decl::getClangNode() except we look through to the 'Code'
279281
/// enum of an error wrapper struct.
280282
ClangNode getEffectiveClangNode(const Decl *decl) const;

lib/ClangImporter/ClangDerivedConformances.cpp

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,32 @@
1717
#include "swift/ClangImporter/ClangImporterRequests.h"
1818

1919
using namespace swift;
20+
using namespace swift::importer;
2021

2122
/// Alternative to `NominalTypeDecl::lookupDirect`.
2223
/// This function does not attempt to load extensions of the nominal decl.
2324
static TinyPtrVector<ValueDecl *>
2425
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}), {});
26+
ASTContext &ctx = decl->getASTContext();
27+
auto *importer = static_cast<ClangImporter *>(ctx.getClangModuleLoader());
28+
29+
TinyPtrVector<ValueDecl *> result;
30+
31+
if (id.isOperator()) {
32+
auto underlyingId =
33+
ctx.getIdentifier(getPrivateOperatorName(std::string(id)));
34+
TinyPtrVector<ValueDecl *> underlyingFuncs = evaluateOrDefault(
35+
ctx.evaluator, ClangRecordMemberLookup({decl, underlyingId}), {});
36+
for (auto it : underlyingFuncs) {
37+
if (auto synthesizedFunc =
38+
importer->getCXXSynthesizedOperatorFunc(cast<FuncDecl>(it)))
39+
result.push_back(synthesizedFunc);
40+
}
41+
} else {
42+
// See if there is a Clang decl with the given name.
43+
result = evaluateOrDefault(ctx.evaluator,
44+
ClangRecordMemberLookup({decl, id}), {});
45+
}
2846

2947
// Check if there are any synthesized Swift members that match the name.
3048
for (auto member : decl->getCurrentMembersWithoutLoading()) {

lib/ClangImporter/ClangImporter.cpp

Lines changed: 31 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -88,17 +88,6 @@ using clang::CompilerInvocation;
8888

8989
#pragma mark Internal data structures
9090

91-
namespace {
92-
static std::string getOperatorNameForToken(std::string OperatorToken) {
93-
#define OVERLOADED_OPERATOR(Name, Spelling, Token, Unary, Binary, MemberOnly) \
94-
if (OperatorToken == Spelling) { \
95-
return #Name; \
96-
};
97-
#include "clang/Basic/OperatorKinds.def"
98-
return "None";
99-
}
100-
} // namespace
101-
10291
namespace {
10392
class HeaderImportCallbacks : public clang::PPCallbacks {
10493
ClangImporter::Implementation &Impl;
@@ -4058,6 +4047,8 @@ bool ClangImporter::Implementation::lookupValue(SwiftLookupTable &table,
40584047

40594048
auto &clangCtx = getClangASTContext();
40604049
auto clangTU = clangCtx.getTranslationUnitDecl();
4050+
auto *importer =
4051+
static_cast<ClangImporter *>(SwiftContext.getClangModuleLoader());
40614052

40624053
bool declFound = false;
40634054

@@ -4075,33 +4066,17 @@ bool ClangImporter::Implementation::lookupValue(SwiftLookupTable &table,
40754066
// If CXXInterop is enabled we need to check the modified operator name as
40764067
// well
40774068
if (SwiftContext.LangOpts.EnableCXXInterop) {
4078-
auto funcBaseName = DeclBaseName(SwiftContext.getIdentifier(
4079-
"__operator" + getOperatorNameForToken(
4080-
name.getBaseName().getIdentifier().str().str())));
4069+
auto funcBaseName =
4070+
DeclBaseName(SwiftContext.getIdentifier(getPrivateOperatorName(
4071+
name.getBaseName().getIdentifier().str().str())));
40814072
for (auto entry : table.lookupMemberOperators(funcBaseName)) {
40824073
if (isVisibleClangEntry(entry)) {
4083-
if (auto func = dyn_cast_or_null<ValueDecl>(
4074+
if (auto func = dyn_cast_or_null<FuncDecl>(
40844075
importDeclReal(entry->getMostRecentDecl(), CurrentVersion))) {
4085-
// `func` is not an operator, it is a regular function which has a
4086-
// name that starts with `__operator`. We were asked for a
4087-
// corresponding synthesized Swift operator, so let's retrieve it.
4088-
4089-
// The synthesized Swift operator was added as an alternative decl
4090-
// for `func`.
4091-
auto alternateDecls = getAlternateDecls(func);
4092-
// Did we actually synthesize an operator for `func`?
4093-
if (alternateDecls.empty())
4094-
continue;
4095-
// If we did, then we should have only synthesized one.
4096-
assert(alternateDecls.size() == 1 &&
4097-
"expected only the synthesized operator as an alternative");
4098-
4099-
auto synthesizedOperator = alternateDecls.front();
4100-
assert(synthesizedOperator->isOperator() &&
4101-
"expected the alternative to be a synthesized operator");
4102-
4103-
consumer.foundDecl(synthesizedOperator,
4104-
DeclVisibilityKind::VisibleAtTopLevel);
4076+
if (auto synthesizedOperator =
4077+
importer->getCXXSynthesizedOperatorFunc(func))
4078+
consumer.foundDecl(synthesizedOperator,
4079+
DeclVisibilityKind::VisibleAtTopLevel);
41054080
}
41064081
}
41074082
}
@@ -6095,6 +6070,27 @@ ClangImporter::getCXXFunctionTemplateSpecialization(SubstitutionMap subst,
60956070
return ConcreteDeclRef(newDecl);
60966071
}
60976072

6073+
FuncDecl *ClangImporter::getCXXSynthesizedOperatorFunc(FuncDecl *decl) {
6074+
// `decl` is not an operator, it is a regular function which has a
6075+
// name that starts with `__operator`. We were asked for a
6076+
// corresponding synthesized Swift operator, so let's retrieve it.
6077+
6078+
// The synthesized Swift operator was added as an alternative decl
6079+
// for `func`.
6080+
auto alternateDecls = Impl.getAlternateDecls(decl);
6081+
// Did we actually synthesize an operator for `func`?
6082+
if (alternateDecls.empty())
6083+
return nullptr;
6084+
// If we did, then we should have only synthesized one.
6085+
assert(alternateDecls.size() == 1 &&
6086+
"expected only the synthesized operator as an alternative");
6087+
6088+
auto synthesizedOperator = alternateDecls.front();
6089+
assert(synthesizedOperator->isOperator() &&
6090+
"expected the alternative to be a synthesized operator");
6091+
return cast<FuncDecl>(synthesizedOperator);
6092+
}
6093+
60986094
bool ClangImporter::isCXXMethodMutating(const clang::CXXMethodDecl *method) {
60996095
if (isa<clang::CXXConstructorDecl>(method) || !method->isConst())
61006096
return true;

lib/ClangImporter/ImporterImpl.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1947,6 +1947,15 @@ inline bool requiresCPlusPlus(const clang::Module *module) {
19471947
});
19481948
}
19491949

1950+
inline std::string getPrivateOperatorName(const std::string &OperatorToken) {
1951+
#define OVERLOADED_OPERATOR(Name, Spelling, Token, Unary, Binary, MemberOnly) \
1952+
if (OperatorToken == Spelling) { \
1953+
return "__operator" #Name; \
1954+
};
1955+
#include "clang/Basic/OperatorKinds.def"
1956+
return "None";
1957+
}
1958+
19501959
}
19511960
}
19521961

test/Interop/Cxx/stdlib/overlay/Inputs/custom-iterator.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,4 +463,37 @@ struct HasNoPlusEqualOperator {
463463
}
464464
};
465465

466+
// MARK: Templated iterators
467+
468+
template <typename T>
469+
struct TemplatedIterator {
470+
T value;
471+
472+
using iterator_category = std::input_iterator_tag;
473+
using value_type = T;
474+
using pointer = T *;
475+
using reference = const T &;
476+
using difference_type = int;
477+
478+
TemplatedIterator(int value) : value(value) {}
479+
TemplatedIterator(const TemplatedIterator &other) = default;
480+
481+
const int &operator*() const { return value; }
482+
483+
TemplatedIterator &operator++() {
484+
value++;
485+
return *this;
486+
}
487+
TemplatedIterator operator++(int) {
488+
auto tmp = ConstIterator(value);
489+
value++;
490+
return tmp;
491+
}
492+
bool operator==(const TemplatedIterator &other) const {
493+
return value == other.value;
494+
}
495+
};
496+
497+
using TemplatedIteratorInt = TemplatedIterator<int>;
498+
466499
#endif // TEST_INTEROP_CXX_STDLIB_INPUTS_CUSTOM_ITERATOR_H

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,3 +85,10 @@
8585
// CHECK-NOT: struct HasNoIncrementOperator : UnsafeCxxInputIterator
8686
// CHECK-NOT: struct HasNoPreIncrementOperator : UnsafeCxxInputIterator
8787
// CHECK-NOT: struct HasNoDereferenceOperator : UnsafeCxxInputIterator
88+
89+
// CHECK: struct __CxxTemplateInst17TemplatedIteratorIiE : UnsafeCxxInputIterator {
90+
// CHECK: var pointee: Int32 { get }
91+
// CHECK: func successor() -> __CxxTemplateInst17TemplatedIteratorIiE
92+
// CHECK: typealias Pointee = Int32
93+
// CHECK: static func == (lhs: __CxxTemplateInst17TemplatedIteratorIiE, other: __CxxTemplateInst17TemplatedIteratorIiE) -> Bool
94+
// CHECK: }

test/Interop/Cxx/stdlib/use-std-map.swift

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,6 @@
55
// Enable this everywhere once we have a solution for modularizing other C++ stdlibs: rdar://87654514
66
// REQUIRES: OS=macosx || OS=linux-gnu
77

8-
// This test is failing in Linux CI currently.
9-
// REQUIRES: rdar102420290
10-
118
import StdlibUnittest
129
import StdMap
1310
import CxxStdlib

0 commit comments

Comments
 (0)