Skip to content

Commit 84e1007

Browse files
authored
Merge pull request #69954 from plotfi/plotfi-swift-virtual-functions
[cxx-interop] Enable virtual function calling from Swift to C++
2 parents 26183b9 + ebe83a4 commit 84e1007

File tree

8 files changed

+98
-12
lines changed

8 files changed

+98
-12
lines changed

include/swift/ClangImporter/ClangImporter.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,9 @@ class ClangImporter final : public ClangModuleLoader {
611611
/// Enable the symbolic import experimental feature for the given callback.
612612
void withSymbolicFeatureEnabled(llvm::function_ref<void(void)> callback);
613613

614+
/// Returns true when the symbolic import experimental feature is enabled.
615+
bool isSymbolicImportEnabled() const;
616+
614617
const clang::TypedefType *getTypeDefForCXXCFOptionsDefinition(
615618
const clang::Decl *candidateDecl) override;
616619

lib/ClangImporter/ClangImporter.cpp

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4785,26 +4785,37 @@ static clang::CXXMethodDecl *synthesizeCxxBaseMethod(
47854785
ReferenceReturnTypeBehaviorForBaseMethodSynthesis
47864786
referenceReturnTypeBehavior =
47874787
ReferenceReturnTypeBehaviorForBaseMethodSynthesis::KeepReference,
4788-
bool forceConstQualifier = false) {
4788+
bool forceConstQualifier = false,
4789+
bool isVirtualCall = false) {
47894790
auto &clangCtx = impl.getClangASTContext();
47904791
auto &clangSema = impl.getClangSema();
4792+
// When emitting symbolic decls, the method might not have a concrete
4793+
// record type as this type.
4794+
if (impl.isSymbolicImportEnabled()
4795+
&& !method->getThisType()->getPointeeCXXRecordDecl()) {
4796+
return nullptr;
4797+
}
47914798

47924799
// Create a new method in the derived class that calls the base method.
47934800
clang::DeclarationName name = method->getNameInfo().getName();
47944801
if (name.isIdentifier()) {
47954802
std::string newName;
47964803
llvm::raw_string_ostream os(newName);
4797-
os << "__synthesizedBaseCall_" << name.getAsIdentifierInfo()->getName();
4804+
os << (isVirtualCall ? "__synthesizedVirtualCall_" :
4805+
"__synthesizedBaseCall_")
4806+
<< name.getAsIdentifierInfo()->getName();
47984807
name = clang::DeclarationName(
47994808
&impl.getClangPreprocessor().getIdentifierTable().get(os.str()));
48004809
} else if (name.getCXXOverloadedOperator() == clang::OO_Subscript) {
48014810
name = clang::DeclarationName(
48024811
&impl.getClangPreprocessor().getIdentifierTable().get(
4803-
"__synthesizedBaseCall_operatorSubscript"));
4812+
(isVirtualCall ? "__synthesizedVirtualCall_operatorSubscript" :
4813+
"__synthesizedBaseCall_operatorSubscript")));
48044814
} else if (name.getCXXOverloadedOperator() == clang::OO_Star) {
48054815
name = clang::DeclarationName(
48064816
&impl.getClangPreprocessor().getIdentifierTable().get(
4807-
"__synthesizedBaseCall_operatorStar"));
4817+
(isVirtualCall ? "__synthesizedVirtualCall_operatorStar" :
4818+
"__synthesizedBaseCall_operatorStar")));
48084819
}
48094820
auto methodType = method->getType();
48104821
// Check if we need to drop the reference from the return type
@@ -4930,6 +4941,16 @@ static clang::CXXMethodDecl *synthesizeCxxBaseMethod(
49304941
return newMethod;
49314942
}
49324943

4944+
// Synthesize a C++ virtual method
4945+
clang::CXXMethodDecl *synthesizeCxxVirtualMethod(
4946+
swift::ClangImporter &Impl, const clang::CXXRecordDecl *derivedClass,
4947+
const clang::CXXRecordDecl *baseClass, const clang::CXXMethodDecl *method) {
4948+
return synthesizeCxxBaseMethod(
4949+
Impl, derivedClass, baseClass, method,
4950+
ReferenceReturnTypeBehaviorForBaseMethodSynthesis::KeepReference,
4951+
false /* forceConstQualifier */, true /* isVirtualCall */);
4952+
}
4953+
49334954
// Find the base C++ method called by the base function we want to synthesize
49344955
// the derived thunk for.
49354956
// The base C++ method is either the original C++ method that corresponds
@@ -6555,7 +6576,7 @@ static ValueDecl *addThunkForDependentTypes(FuncDecl *oldDecl,
65556576
// are not used in the function signature. We supply the type params as explicit
65566577
// metatype arguments to aid in typechecking, but they shouldn't be forwarded to
65576578
// the corresponding C++ function.
6558-
static std::pair<BraceStmt *, bool>
6579+
std::pair<BraceStmt *, bool>
65596580
synthesizeForwardingThunkBody(AbstractFunctionDecl *afd, void *context) {
65606581
ASTContext &ctx = afd->getASTContext();
65616582

@@ -7439,6 +7460,10 @@ void ClangImporter::withSymbolicFeatureEnabled(
74397460
oldImportSymbolicCXXDecls.get());
74407461
}
74417462

7463+
bool ClangImporter::isSymbolicImportEnabled() const {
7464+
return Impl.importSymbolicCXXDecls;
7465+
}
7466+
74427467
const clang::TypedefType *ClangImporter::getTypeDefForCXXCFOptionsDefinition(
74437468
const clang::Decl *candidateDecl) {
74447469

lib/ClangImporter/ImportDecl.cpp

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3711,9 +3711,33 @@ namespace {
37113711
Decl *VisitCXXMethodDecl(const clang::CXXMethodDecl *decl) {
37123712
auto method = VisitFunctionDecl(decl);
37133713
if (decl->isVirtual() && isa_and_nonnull<ValueDecl>(method)) {
3714+
3715+
if (auto dc = method->getDeclContext();
3716+
!decl->isPure() &&
3717+
isa_and_nonnull<NominalTypeDecl>(dc->getAsDecl())) {
3718+
3719+
// generates the __synthesizedVirtualCall_ C++ thunk
3720+
clang::CXXMethodDecl *cxxThunk = synthesizeCxxVirtualMethod(
3721+
*static_cast<ClangImporter *>(
3722+
dc->getASTContext().getClangModuleLoader()),
3723+
decl->getParent(), decl->getParent(), decl);
3724+
3725+
// call the __synthesizedVirtualCall_ C++ thunk from a Swift thunk
3726+
if (Decl *swiftThunk =
3727+
cxxThunk ? VisitCXXMethodDecl(cxxThunk) : nullptr;
3728+
isa_and_nonnull<FuncDecl>(swiftThunk)) {
3729+
// synthesize the body of the Swift method to call the swiftThunk
3730+
synthesizeForwardingThunkBody(cast<FuncDecl>(method),
3731+
cast<FuncDecl>(swiftThunk));
3732+
return method;
3733+
}
3734+
}
3735+
37143736
Impl.markUnavailable(
37153737
cast<ValueDecl>(method),
3716-
"virtual functions are not yet available in Swift");
3738+
decl->isPure() ?
3739+
"virtual function is not available in Swift because it is pure" :
3740+
"virtual function is not available in Swift");
37173741
}
37183742

37193743
if (Impl.SwiftContext.LangOpts.CxxInteropGettersSettersAsProperties ||

lib/ClangImporter/ImporterImpl.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1988,4 +1988,14 @@ inline std::string getPrivateOperatorName(const std::string &OperatorToken) {
19881988
}
19891989
}
19901990

1991+
// Forwards to synthesizeCxxBasicMethod(), producing a thunk that calls a
1992+
// virtual function.
1993+
clang::CXXMethodDecl *synthesizeCxxVirtualMethod(
1994+
swift::ClangImporter &Impl, const clang::CXXRecordDecl *derivedClass,
1995+
const clang::CXXRecordDecl *baseClass, const clang::CXXMethodDecl *method);
1996+
1997+
// Exposed to produce a Swift method body for calling a Swift thunk.
1998+
std::pair<swift::BraceStmt *, bool>
1999+
synthesizeForwardingThunkBody(swift::AbstractFunctionDecl *afd, void *context);
2000+
19912001
#endif

test/Interop/Cxx/class/inheritance/Inputs/virtual-methods.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ struct Base {
88
virtual void foo() = 0;
99
};
1010

11+
struct Base2 { virtual int f() = 0; };
12+
struct Base3 { virtual int f() { return 24; } };
13+
struct Derived2 : public Base2 { virtual int f() { return 42; } };
14+
struct Derived3 : public Base3 { virtual int f() { return 42; } };
15+
struct Derived4 : public Base3 { };
16+
1117
template <class T>
1218
struct Derived : Base {
1319
inline void foo() override {

test/Interop/Cxx/class/inheritance/virtual-methods-irgen.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,24 @@ import VirtualMethods
88
var x = DerivedInt()
99
x.callMe()
1010

11+
var b3 = Base3()
12+
var d2 = Derived2()
13+
var d3 = Derived3()
14+
var d4 = Derived4()
15+
16+
b3.f()
17+
d2.f()
18+
d3.f()
19+
d4.f()
20+
21+
// CHECK: invoke {{.*}} @_ZN5Base31fEv
22+
// CHECK: invoke {{.*}} @_ZN8Derived21fEv
23+
// CHECK: invoke {{.*}} @_ZN8Derived31fEv
24+
// CHECK: call swiftcc {{.*}} @"$sSo8Derived4V1fs5Int32VyF"
25+
26+
// CHECK: define {{.*}} @"$sSo8Derived4V1fs5Int32VyF"(ptr swiftself dereferenceable
27+
// CHECK: invoke {{.*}} @_ZN8Derived423__synthesizedBaseCall_fEv
28+
1129
// CHECK: define {{.*}}void @{{_ZN7DerivedIiE3fooEv|"\?foo@\?$Derived@H@@UEAAXXZ"}}
1230
// CHECK: call void @{{_Z21testFunctionCollectedv|"\?testFunctionCollected@@YAXXZ"}}
1331

test/Interop/Cxx/class/inheritance/virtual-methods-module-interface.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22

33
// CHECK: struct Base {
44
// CHECK-NEXT: init()
5-
// CHECK-NEXT: @available(*, unavailable, message: "virtual functions are not yet available in Swift")
5+
// CHECK-NEXT: @available(*, unavailable, message: "virtual function is not available in Swift because it is pure")
66
// CHECK-NEXT: mutating func foo()
77

88
// CHECK: struct Derived<CInt> {
9-
// CHECK: @available(*, unavailable, message: "virtual functions are not yet available in Swift")
10-
// CHECK: mutating func foo()
9+
// CHECK-NEXT: init()
10+
// CHECK-NEXT: mutating func foo()
1111
// CHECK: }
1212

1313
// CHECK: struct VirtualNonAbstractBase {
14-
// CHECK: @available(*, unavailable, message: "virtual functions are not yet available in Swift")
15-
// CHECK: func nonAbstractMethod()
14+
// CHECK-NEXT: init()
15+
// CHECK-NEXT: func nonAbstractMethod()

test/Interop/Cxx/class/inheritance/virtual-methods-typechecker.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22

33
import VirtualMethods
44

5-
VirtualNonAbstractBase().nonAbstractMethod() // expected-error {{'nonAbstractMethod()' is unavailable: virtual functions are not yet available in Swift}}
5+
VirtualNonAbstractBase().nonAbstractMethod()

0 commit comments

Comments
 (0)