Skip to content

Commit 585ca5e

Browse files
committed
[cxx-interop] Adding swift_name attributes to virtual methods overrides
1 parent 21ded5c commit 585ca5e

11 files changed

+531
-12
lines changed

include/swift/AST/DiagnosticsClangImporter.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,11 @@ GROUPED_WARNING(inconsistent_swift_name, ClangDeclarationImport, none,
7373
(bool, StringRef, StringRef, DeclName, StringRef, DeclName,
7474
StringRef))
7575

76+
WARNING(swift_name_attr_ignored, none,
77+
"ignoring swift_name attribute %0; swift_name attributes have no "
78+
"effect on method overrides",
79+
(DeclName))
80+
7681
GROUPED_WARNING(swift_name_circular_context_import, ClangDeclarationImport, none,
7782
"cycle detected while resolving '%0' in swift_name attribute for '%1'",
7883
(StringRef, StringRef))

lib/ClangImporter/ClangImporter.cpp

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6336,11 +6336,9 @@ TinyPtrVector<ValueDecl *> ClangRecordMemberLookup::evaluate(
63366336
// Capture the arity of already found members in the
63376337
// current record, to avoid adding ambiguous members
63386338
// from base classes.
6339-
const auto getArity =
6340-
ClangImporter::Implementation::getImportedBaseMemberDeclArity;
6341-
llvm::SmallSet<size_t, 4> foundNameArities;
6339+
llvm::SmallSet<DeclName, 4> foundMethodNames;
63426340
for (const auto *valueDecl : result)
6343-
foundNameArities.insert(getArity(valueDecl));
6341+
foundMethodNames.insert(valueDecl->getName());
63446342

63456343
for (auto base : cxxRecord->bases()) {
63466344
if (skipIfNonPublic && base.getAccessSpecifier() != clang::AS_public)
@@ -6374,9 +6372,9 @@ TinyPtrVector<ValueDecl *> ClangRecordMemberLookup::evaluate(
63746372
{});
63756373

63766374
for (auto foundInBase : baseResults) {
6377-
// Do not add duplicate entry with the same arity,
6375+
// Do not add duplicate entry with the same DeclName,
63786376
// as that would cause an ambiguous lookup.
6379-
if (foundNameArities.count(getArity(foundInBase)))
6377+
if (foundMethodNames.count(foundInBase->getName()))
63806378
continue;
63816379

63826380
collector.add(foundInBase);
@@ -7671,6 +7669,7 @@ ValueDecl *ClangImporter::Implementation::importBaseMemberDecl(
76717669
auto known = clonedBaseMembers.find(key);
76727670
if (known == clonedBaseMembers.end()) {
76737671
ValueDecl *cloned = cloneBaseMemberDecl(decl, newContext, inheritance);
7672+
handleAmbiguousSwiftName(cloned);
76747673
known = clonedBaseMembers.insert({key, cloned}).first;
76757674
clonedMembers.insert(std::make_pair(cloned, decl));
76767675
}

lib/ClangImporter/ImportDecl.cpp

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
#include "swift/ClangImporter/ClangImporterRequests.h"
5656
#include "swift/ClangImporter/ClangModule.h"
5757
#include "swift/Parse/Lexer.h"
58+
#include "swift/Parse/ParseDeclName.h"
5859
#include "swift/Strings.h"
5960
#include "clang/AST/ASTContext.h"
6061
#include "clang/AST/Attr.h"
@@ -4424,7 +4425,43 @@ namespace {
44244425
// Create a thunk that will perform dynamic dispatch.
44254426
// TODO: we don't have to import the actual `method` in this case,
44264427
// we can just synthesize a thunk and import that instead.
4427-
auto result = synthesizer.makeVirtualMethod(decl);
4428+
4429+
FuncDecl *result;
4430+
if (decl->size_overridden_methods() > 0) {
4431+
if (auto swiftNameAttr = decl->getAttr<clang::SwiftNameAttr>()) {
4432+
auto parsedDeclName = parseDeclName(swiftNameAttr->getName());
4433+
auto swiftDeclName =
4434+
parsedDeclName.formDeclName(method->getASTContext());
4435+
ImportedName importedName;
4436+
std::tie(importedName, std::ignore) = importFullName(decl);
4437+
4438+
result = synthesizer.makeVirtualMethod(decl);
4439+
4440+
if (swiftDeclName != importedName.getDeclName()) {
4441+
Impl.diagnose(HeaderLoc(swiftNameAttr->getLoc()),
4442+
diag::swift_name_attr_ignored, swiftDeclName);
4443+
4444+
Impl.markUnavailable(
4445+
result, (llvm::Twine("ignoring swift_name '") +
4446+
swiftNameAttr->getName() + "' in '" +
4447+
decl->getParent()->getName() +
4448+
"'; swift_name attributes have no effect "
4449+
"on method overrides")
4450+
.str());
4451+
}
4452+
} else {
4453+
// If there's no swift_name attribute, we don't import this method.
4454+
// This is because if the overridden method was renamed and
4455+
// this one is not, we want to use the overridden method's name.
4456+
// This is reasonable because `makeVirtualMethod` returns
4457+
// a thunk that will perform dynamic dispatch, and consequently
4458+
// the correct instance of the method will get executed.
4459+
return nullptr;
4460+
}
4461+
} else {
4462+
result = synthesizer.makeVirtualMethod(decl);
4463+
}
4464+
44284465
if (result) {
44294466
return result;
44304467
} else {
@@ -10367,6 +10404,22 @@ ValueDecl *ClangImporter::Implementation::createUnavailableDecl(
1036710404
return var;
1036810405
}
1036910406

10407+
void ClangImporter::Implementation::handleAmbiguousSwiftName(ValueDecl *decl) {
10408+
if (!decl || decl->isUnavailable())
10409+
return;
10410+
10411+
auto *cxxRecordDecl = dyn_cast<clang::CXXRecordDecl>(
10412+
decl->getDeclContext()->getAsDecl()->getClangDecl());
10413+
10414+
if (!cxxRecordDecl)
10415+
return;
10416+
10417+
if (findUnavailableMethod(cxxRecordDecl, decl->getName())) {
10418+
markUnavailable(decl,
10419+
"overrides multiple C++ methods with different Swift names");
10420+
}
10421+
}
10422+
1037010423
// Force the members of the entire inheritance hierarchy to be loaded and
1037110424
// deserialized before loading the members of this class. This allows the
1037210425
// decl members table to be warmed up and enables the correct identification of

lib/ClangImporter/ImportName.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1706,6 +1706,12 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
17061706
}
17071707
}
17081708

1709+
// `swift_name` attribute is not supported in virtual methods overrides
1710+
if (auto method = dyn_cast<clang::CXXMethodDecl>(D)) {
1711+
if (method->isVirtual() && method->size_overridden_methods() > 0)
1712+
skipCustomName = true;
1713+
}
1714+
17091715
if (!skipCustomName) {
17101716
result.info.hasCustomName = true;
17111717
result.declName = parsedName.formDeclName(
@@ -2315,6 +2321,42 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
23152321
newName = baseName.substr(StringRef("__synthesizedVirtualCall_").size());
23162322
baseName = newName;
23172323
}
2324+
if (method->isVirtual()) {
2325+
// The name should be imported from the base method
2326+
if (method->size_overridden_methods() > 0) {
2327+
DeclName overriddenName;
2328+
bool foundDivergentMethod = false;
2329+
for (auto overriddenMethod : method->overridden_methods()) {
2330+
ImportedName importedName =
2331+
importName(overriddenMethod, version, givenName);
2332+
if (!overriddenName) {
2333+
overriddenName = importedName.getDeclName();
2334+
} else if (overriddenName.compare(importedName.getDeclName())) {
2335+
importerImpl->insertUnavailableMethod(method->getParent(),
2336+
importedName.getDeclName());
2337+
foundDivergentMethod = true;
2338+
}
2339+
}
2340+
2341+
if (foundDivergentMethod) {
2342+
// The method we want to mark as unavailable will be generated
2343+
// lazily, when we clone the methods from base classes to the derived
2344+
// class method->getParent().
2345+
// Since we don't have the actual method here, we store this
2346+
// information to be accessed when we generate the actual method.
2347+
importerImpl->insertUnavailableMethod(method->getParent(),
2348+
overriddenName);
2349+
return ImportedName();
2350+
}
2351+
2352+
baseName = overriddenName.getBaseIdentifier().str();
2353+
// Also inherit argument names from base method
2354+
argumentNames.clear();
2355+
llvm::for_each(overriddenName.getArgumentNames(), [&](Identifier arg) {
2356+
argumentNames.push_back(arg.str());
2357+
});
2358+
}
2359+
}
23182360
}
23192361

23202362
// swift_newtype-ed declarations may have common words with the type name

lib/ClangImporter/ImporterImpl.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -694,6 +694,14 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
694694
// Map all cloned methods back to the original member
695695
llvm::DenseMap<ValueDecl *, ValueDecl *> clonedMembers;
696696

697+
// Keep track of methods that are unavailale in each class.
698+
// We need this set because these methods will be imported lazily. We don't
699+
// have the corresponding Swift method when the availability check is
700+
// performed, so instead we store the information in this set and then, when
701+
// the method is finally generated, we check if it's present here
702+
llvm::DenseSet<std::pair<const clang::CXXRecordDecl *, DeclName>>
703+
unavailableMethods;
704+
697705
public:
698706
llvm::DenseMap<const clang::ParmVarDecl*, FuncDecl*> defaultArgGenerators;
699707

@@ -722,6 +730,18 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
722730
return getNameImporter().getEnumKind(decl);
723731
}
724732

733+
bool findUnavailableMethod(const clang::CXXRecordDecl *classDecl,
734+
DeclName name) {
735+
return unavailableMethods.contains({classDecl, name});
736+
}
737+
738+
void insertUnavailableMethod(const clang::CXXRecordDecl *classDecl,
739+
DeclName name) {
740+
unavailableMethods.insert({classDecl, name});
741+
}
742+
743+
void handleAmbiguousSwiftName(ValueDecl *decl);
744+
725745
private:
726746
/// A mapping from imported declarations to their "alternate" declarations,
727747
/// for cases where a single Clang declaration is imported to two

lib/ClangImporter/SwiftLookupTable.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "swift/Basic/STLExtras.h"
2323
#include "swift/Basic/Version.h"
2424
#include "swift/ClangImporter/ClangImporter.h"
25+
#include "swift/Parse/ParseDeclName.h"
2526
#include "clang/AST/DeclCXX.h"
2627
#include "clang/AST/DeclObjC.h"
2728
#include "clang/Lex/MacroInfo.h"
@@ -1945,6 +1946,15 @@ void importer::addEntryToLookupTable(SwiftLookupTable &table,
19451946
named, importedName.getEffectiveContext());
19461947
}
19471948

1949+
if (auto swiftNameAttr = named->getAttr<clang::SwiftNameAttr>()) {
1950+
auto parsedDeclName = parseDeclName(swiftNameAttr->getName());
1951+
auto swiftDeclName =
1952+
parsedDeclName.formDeclName(nameImporter.getContext());
1953+
if (importedName.getDeclName() != swiftDeclName)
1954+
table.addEntry(swiftDeclName, named,
1955+
importedName.getEffectiveContext());
1956+
}
1957+
19481958
return true;
19491959
});
19501960
if (failed) {

test/IDE/dump_swift_lookup_tables.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,19 +41,19 @@ import ImportAsMember
4141
// CHECK-NEXT: TU: __swift
4242
// CHECK-NEXT: adding:
4343
// CHECK-NEXT: SNSomeStruct: SNAdding
44-
// CHECK-NEXT: blue:
44+
// CHECK: blue:
4545
// CHECK-NEXT: SNColorChoice: SNColorBlue
4646
// CHECK-NEXT: defaultValue:
4747
// CHECK-NEXT: SNSomeStruct: SNSomeStructGetDefault, SNSomeStructSetDefault
48-
// CHECK-NEXT: defaultX:
48+
// CHECK: defaultX:
4949
// CHECK-NEXT: SNSomeStruct: DefaultXValue
50-
// CHECK-NEXT: foo:
50+
// CHECK: foo:
5151
// CHECK-NEXT: SNSomeStruct: SNSomeStructGetFoo, SNSomeStructSetFoo
52-
// CHECK-NEXT: green:
52+
// CHECK: green:
5353
// CHECK-NEXT: SNColorChoice: SNColorGreen
5454
// CHECK-NEXT: init:
5555
// CHECK-NEXT: SNSomeStruct: SNCreate
56-
// CHECK-NEXT: makeSomeStruct:
56+
// CHECK: makeSomeStruct:
5757
// CHECK-NEXT: TU: SNMakeSomeStruct, SNMakeSomeStructForX
5858
// CHECK-NEXT: x:
5959
// CHECK-NEXT: SNSomeStruct: X

test/Interop/Cxx/foreign-reference/Inputs/member-inheritance.h

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,3 +139,138 @@ inline const Immortal *_Nonnull castToImmortal(
139139
const DerivedFromImmortal *_Nonnull immortal) {
140140
return static_cast<const Immortal *>(immortal);
141141
}
142+
143+
// A1
144+
// / \
145+
// B1 B2
146+
// / \
147+
// C1 C2
148+
149+
struct IMMORTAL_FRT A1 {
150+
virtual int virtualMethod() const { return 111; }
151+
152+
__attribute__((swift_name("swiftFooRename()")))
153+
virtual int fooRename() const { return 112; }
154+
155+
__attribute__((swift_name("swiftBarRename()")))
156+
virtual int barRename() const { return 113; }
157+
158+
__attribute__((swift_name("swiftParamsRename(a1:)")))
159+
virtual int paramsRename(int i) const { return i; }
160+
161+
static A1 *_Nonnull create() { return new A1(); }
162+
};
163+
164+
struct B1 : A1 {
165+
__attribute__((swift_name("swiftVirtualMethod()")))
166+
virtual int virtualMethod() const override { return 211; }
167+
168+
virtual int fooRename() const override { return 212; }
169+
170+
__attribute__((swift_name("B1BarRename()")))
171+
virtual int barRename() const override { return 213; }
172+
173+
__attribute__((swift_name("swiftParamsRename(b1:)")))
174+
virtual int paramsRename(int i) const override { return i; }
175+
176+
static B1 *_Nonnull create() { return new B1(); }
177+
};
178+
179+
struct B2 : A1 {
180+
int virtualMethod() const { return 221; }
181+
182+
int fooRename() const { return 222; }
183+
184+
__attribute__((swift_name("B2BarRename()"))) int barRename() const {
185+
return 223;
186+
}
187+
188+
static B2 *_Nonnull create() { return new B2(); }
189+
};
190+
191+
struct C1 : B1 {
192+
__attribute__((swift_name("swiftFooRename()")))
193+
virtual int fooRename() const override { return 312; }
194+
195+
__attribute__((swift_name("swiftBarRename()")))
196+
virtual int barRename() const override { return 313; }
197+
198+
virtual int paramsRename(int i) const override { return i; }
199+
200+
static C1 *_Nonnull create() { return new C1(); }
201+
};
202+
203+
struct C2 : B1 {
204+
__attribute__((swift_name("swiftVirtualMethod()")))
205+
virtual int virtualMethod() const override { return 321; }
206+
207+
__attribute__((swift_name("C2FooRename()")))
208+
virtual int fooRename() const override { return 322; }
209+
210+
__attribute__((swift_name("B1BarRename()")))
211+
virtual int barRename() const override { return 323; }
212+
213+
__attribute__((swift_name("swiftParamsRename(b1:)")))
214+
virtual int paramsRename(int i) const override { return i; }
215+
216+
static C2 *_Nonnull create() { return new C2(); }
217+
};
218+
219+
struct IMMORTAL_FRT A2 {
220+
__attribute__((swift_name("swiftVirtualMethod()")))
221+
virtual int virtualMethod() const { return 121; }
222+
223+
__attribute__((swift_name("swiftFooRename()")))
224+
virtual int fooRename() const { return 122; }
225+
226+
__attribute__((swift_name("A2BarRename()")))
227+
virtual int barRename() const { return 123; }
228+
229+
__attribute__((swift_name("swiftParamsRename(a2:)"))) virtual int
230+
paramsRename(int i) const {
231+
return i + 1;
232+
}
233+
234+
static A2 *_Nonnull create() { return new A2(); }
235+
};
236+
237+
// A1 A2
238+
// \ /
239+
// D1
240+
241+
struct D1 : A1, A2 {
242+
static D1 *_Nonnull create() { return new D1(); }
243+
};
244+
245+
// A1 A2
246+
// \ /
247+
// B1 /
248+
// \ /
249+
// D2
250+
251+
struct D2 : B1, A2 {
252+
__attribute__((swift_name("swiftVirtualMethod()")))
253+
virtual int virtualMethod() const override { return 411; }
254+
255+
virtual int fooRename() const override { return 412; }
256+
257+
virtual int barRename() const override { return 413; }
258+
259+
virtual int paramsRename(int i) const override { return i; }
260+
};
261+
262+
// A1
263+
// / \
264+
// / \
265+
// B1 B2
266+
// |\ /|
267+
// | \ / |
268+
// | D3 |
269+
// C1 |
270+
// \ |
271+
// \ /
272+
// D4
273+
274+
struct D3 : B1, B2 {};
275+
276+
struct D4 : C1, B2 {};

0 commit comments

Comments
 (0)