Skip to content

Commit 0337f7e

Browse files
authored
Merge pull request #83067 from susmonteiro/rename-virtual-methods
[cxx-interop] Adding swift_name attributes to virtual methods overrides
2 parents 8a689bb + 585ca5e commit 0337f7e

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
@@ -6351,11 +6351,9 @@ TinyPtrVector<ValueDecl *> ClangRecordMemberLookup::evaluate(
63516351
// Capture the arity of already found members in the
63526352
// current record, to avoid adding ambiguous members
63536353
// from base classes.
6354-
const auto getArity =
6355-
ClangImporter::Implementation::getImportedBaseMemberDeclArity;
6356-
llvm::SmallSet<size_t, 4> foundNameArities;
6354+
llvm::SmallSet<DeclName, 4> foundMethodNames;
63576355
for (const auto *valueDecl : result)
6358-
foundNameArities.insert(getArity(valueDecl));
6356+
foundMethodNames.insert(valueDecl->getName());
63596357

63606358
for (auto base : cxxRecord->bases()) {
63616359
if (skipIfNonPublic && base.getAccessSpecifier() != clang::AS_public)
@@ -6389,9 +6387,9 @@ TinyPtrVector<ValueDecl *> ClangRecordMemberLookup::evaluate(
63896387
{});
63906388

63916389
for (auto foundInBase : baseResults) {
6392-
// Do not add duplicate entry with the same arity,
6390+
// Do not add duplicate entry with the same DeclName,
63936391
// as that would cause an ambiguous lookup.
6394-
if (foundNameArities.count(getArity(foundInBase)))
6392+
if (foundMethodNames.count(foundInBase->getName()))
63956393
continue;
63966394

63976395
collector.add(foundInBase);
@@ -7694,6 +7692,7 @@ ValueDecl *ClangImporter::Implementation::importBaseMemberDecl(
76947692
auto known = clonedBaseMembers.find(key);
76957693
if (known == clonedBaseMembers.end()) {
76967694
ValueDecl *cloned = cloneBaseMemberDecl(decl, newContext, inheritance);
7695+
handleAmbiguousSwiftName(cloned);
76977696
known = clonedBaseMembers.insert({key, cloned}).first;
76987697
clonedMembers.insert(std::make_pair(cloned, decl));
76997698
}

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"
@@ -4426,7 +4427,43 @@ namespace {
44264427
// Create a thunk that will perform dynamic dispatch.
44274428
// TODO: we don't have to import the actual `method` in this case,
44284429
// we can just synthesize a thunk and import that instead.
4429-
auto result = synthesizer.makeVirtualMethod(decl);
4430+
4431+
FuncDecl *result;
4432+
if (decl->size_overridden_methods() > 0) {
4433+
if (auto swiftNameAttr = decl->getAttr<clang::SwiftNameAttr>()) {
4434+
auto parsedDeclName = parseDeclName(swiftNameAttr->getName());
4435+
auto swiftDeclName =
4436+
parsedDeclName.formDeclName(method->getASTContext());
4437+
ImportedName importedName;
4438+
std::tie(importedName, std::ignore) = importFullName(decl);
4439+
4440+
result = synthesizer.makeVirtualMethod(decl);
4441+
4442+
if (swiftDeclName != importedName.getDeclName()) {
4443+
Impl.diagnose(HeaderLoc(swiftNameAttr->getLoc()),
4444+
diag::swift_name_attr_ignored, swiftDeclName);
4445+
4446+
Impl.markUnavailable(
4447+
result, (llvm::Twine("ignoring swift_name '") +
4448+
swiftNameAttr->getName() + "' in '" +
4449+
decl->getParent()->getName() +
4450+
"'; swift_name attributes have no effect "
4451+
"on method overrides")
4452+
.str());
4453+
}
4454+
} else {
4455+
// If there's no swift_name attribute, we don't import this method.
4456+
// This is because if the overridden method was renamed and
4457+
// this one is not, we want to use the overridden method's name.
4458+
// This is reasonable because `makeVirtualMethod` returns
4459+
// a thunk that will perform dynamic dispatch, and consequently
4460+
// the correct instance of the method will get executed.
4461+
return nullptr;
4462+
}
4463+
} else {
4464+
result = synthesizer.makeVirtualMethod(decl);
4465+
}
4466+
44304467
if (result) {
44314468
return result;
44324469
} else {
@@ -10377,6 +10414,22 @@ ValueDecl *ClangImporter::Implementation::createUnavailableDecl(
1037710414
return var;
1037810415
}
1037910416

10417+
void ClangImporter::Implementation::handleAmbiguousSwiftName(ValueDecl *decl) {
10418+
if (!decl || decl->isUnavailable())
10419+
return;
10420+
10421+
auto *cxxRecordDecl = dyn_cast<clang::CXXRecordDecl>(
10422+
decl->getDeclContext()->getAsDecl()->getClangDecl());
10423+
10424+
if (!cxxRecordDecl)
10425+
return;
10426+
10427+
if (findUnavailableMethod(cxxRecordDecl, decl->getName())) {
10428+
markUnavailable(decl,
10429+
"overrides multiple C++ methods with different Swift names");
10430+
}
10431+
}
10432+
1038010433
// Force the members of the entire inheritance hierarchy to be loaded and
1038110434
// deserialized before loading the members of this class. This allows the
1038210435
// 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
@@ -699,6 +699,14 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
699699
// Map all cloned methods back to the original member
700700
llvm::DenseMap<ValueDecl *, ValueDecl *> clonedMembers;
701701

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

@@ -727,6 +735,18 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
727735
return getNameImporter().getEnumKind(decl);
728736
}
729737

738+
bool findUnavailableMethod(const clang::CXXRecordDecl *classDecl,
739+
DeclName name) {
740+
return unavailableMethods.contains({classDecl, name});
741+
}
742+
743+
void insertUnavailableMethod(const clang::CXXRecordDecl *classDecl,
744+
DeclName name) {
745+
unavailableMethods.insert({classDecl, name});
746+
}
747+
748+
void handleAmbiguousSwiftName(ValueDecl *decl);
749+
730750
private:
731751
/// A mapping from imported declarations to their "alternate" declarations,
732752
/// 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"
@@ -1921,6 +1922,15 @@ void importer::addEntryToLookupTable(SwiftLookupTable &table,
19211922
named, importedName.getEffectiveContext());
19221923
}
19231924

1925+
if (auto swiftNameAttr = named->getAttr<clang::SwiftNameAttr>()) {
1926+
auto parsedDeclName = parseDeclName(swiftNameAttr->getName());
1927+
auto swiftDeclName =
1928+
parsedDeclName.formDeclName(nameImporter.getContext());
1929+
if (importedName.getDeclName() != swiftDeclName)
1930+
table.addEntry(swiftDeclName, named,
1931+
importedName.getEffectiveContext());
1932+
}
1933+
19241934
return true;
19251935
});
19261936
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)