mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[cxx-interop] Enable virtual function calling from Swift to C++
This is a forward-interop feature that wires up existing functionality for
synthesizing base class function calling to enable virtual function calling.
The general idea is to sythesize the pattern:
```
// C++ class:
struct S { virtual auto f() -> int { return 42; } };
// Swift User:
var s = S()
print("42: \(s.f())")
// Synthetized Swift Code:
extension S { func f() -> CInt { __synthesizedVirtualCall_f() } }
// Synthetized C/C++ Code:
auto __cxxVirtualCall_f(S *s) -> int { return s->f(); }
```
The idea here is to allow for the synthetized C++ bits from the Clang side to
handle the complexity of virtual function calling.
This commit is contained in:
@@ -4785,7 +4785,8 @@ static clang::CXXMethodDecl *synthesizeCxxBaseMethod(
|
||||
ReferenceReturnTypeBehaviorForBaseMethodSynthesis
|
||||
referenceReturnTypeBehavior =
|
||||
ReferenceReturnTypeBehaviorForBaseMethodSynthesis::KeepReference,
|
||||
bool forceConstQualifier = false) {
|
||||
bool forceConstQualifier = false,
|
||||
bool isVirtualCall = false) {
|
||||
auto &clangCtx = impl.getClangASTContext();
|
||||
auto &clangSema = impl.getClangSema();
|
||||
|
||||
@@ -4794,17 +4795,21 @@ static clang::CXXMethodDecl *synthesizeCxxBaseMethod(
|
||||
if (name.isIdentifier()) {
|
||||
std::string newName;
|
||||
llvm::raw_string_ostream os(newName);
|
||||
os << "__synthesizedBaseCall_" << name.getAsIdentifierInfo()->getName();
|
||||
os << (isVirtualCall ? "__synthesizedVirtualCall_" :
|
||||
"__synthesizedBaseCall_")
|
||||
<< name.getAsIdentifierInfo()->getName();
|
||||
name = clang::DeclarationName(
|
||||
&impl.getClangPreprocessor().getIdentifierTable().get(os.str()));
|
||||
} else if (name.getCXXOverloadedOperator() == clang::OO_Subscript) {
|
||||
name = clang::DeclarationName(
|
||||
&impl.getClangPreprocessor().getIdentifierTable().get(
|
||||
"__synthesizedBaseCall_operatorSubscript"));
|
||||
(isVirtualCall ? "__synthesizedVirtualCall_operatorSubscript" :
|
||||
"__synthesizedBaseCall_operatorSubscript")));
|
||||
} else if (name.getCXXOverloadedOperator() == clang::OO_Star) {
|
||||
name = clang::DeclarationName(
|
||||
&impl.getClangPreprocessor().getIdentifierTable().get(
|
||||
"__synthesizedBaseCall_operatorStar"));
|
||||
(isVirtualCall ? "__synthesizedVirtualCall_operatorStar" :
|
||||
"__synthesizedBaseCall_operatorStar")));
|
||||
}
|
||||
auto methodType = method->getType();
|
||||
// Check if we need to drop the reference from the return type
|
||||
@@ -4930,6 +4935,16 @@ static clang::CXXMethodDecl *synthesizeCxxBaseMethod(
|
||||
return newMethod;
|
||||
}
|
||||
|
||||
// Synthesize a C++ virtual method
|
||||
clang::CXXMethodDecl *synthesizeCxxVirtualMethod(
|
||||
swift::ClangImporter &Impl, const clang::CXXRecordDecl *derivedClass,
|
||||
const clang::CXXRecordDecl *baseClass, const clang::CXXMethodDecl *method) {
|
||||
return synthesizeCxxBaseMethod(
|
||||
Impl, derivedClass, baseClass, method,
|
||||
ReferenceReturnTypeBehaviorForBaseMethodSynthesis::KeepReference,
|
||||
false /* forceConstQualifier */, true /* isVirtualCall */);
|
||||
}
|
||||
|
||||
// Find the base C++ method called by the base function we want to synthesize
|
||||
// the derived thunk for.
|
||||
// The base C++ method is either the original C++ method that corresponds
|
||||
@@ -6555,7 +6570,7 @@ static ValueDecl *addThunkForDependentTypes(FuncDecl *oldDecl,
|
||||
// are not used in the function signature. We supply the type params as explicit
|
||||
// metatype arguments to aid in typechecking, but they shouldn't be forwarded to
|
||||
// the corresponding C++ function.
|
||||
static std::pair<BraceStmt *, bool>
|
||||
std::pair<BraceStmt *, bool>
|
||||
synthesizeForwardingThunkBody(AbstractFunctionDecl *afd, void *context) {
|
||||
ASTContext &ctx = afd->getASTContext();
|
||||
|
||||
|
||||
@@ -3711,9 +3711,32 @@ namespace {
|
||||
Decl *VisitCXXMethodDecl(const clang::CXXMethodDecl *decl) {
|
||||
auto method = VisitFunctionDecl(decl);
|
||||
if (decl->isVirtual() && isa_and_nonnull<ValueDecl>(method)) {
|
||||
|
||||
if (auto dc = method->getDeclContext();
|
||||
!decl->isPure() &&
|
||||
isa_and_nonnull<NominalTypeDecl>(dc->getAsDecl())) {
|
||||
|
||||
// generates the __synthesizedVirtualCall_ C++ thunk
|
||||
clang::CXXMethodDecl *cxxThunk = synthesizeCxxVirtualMethod(
|
||||
*static_cast<ClangImporter *>(
|
||||
dc->getASTContext().getClangModuleLoader()),
|
||||
decl->getParent(), decl->getParent(), decl);
|
||||
|
||||
// call the __synthesizedVirtualCall_ C++ thunk from a Swift thunk
|
||||
if (Decl *swiftThunk = VisitCXXMethodDecl(cxxThunk);
|
||||
isa_and_nonnull<FuncDecl>(swiftThunk)) {
|
||||
// synthesize the body of the Swift method to call the swiftThunk
|
||||
synthesizeForwardingThunkBody(cast<FuncDecl>(method),
|
||||
cast<FuncDecl>(swiftThunk));
|
||||
return method;
|
||||
}
|
||||
}
|
||||
|
||||
Impl.markUnavailable(
|
||||
cast<ValueDecl>(method),
|
||||
"virtual functions are not yet available in Swift");
|
||||
decl->isPure() ?
|
||||
"virtual function is not available in Swift because it is pure" :
|
||||
"virtual function is not available in Swift");
|
||||
}
|
||||
|
||||
if (Impl.SwiftContext.LangOpts.CxxInteropGettersSettersAsProperties ||
|
||||
|
||||
@@ -1988,4 +1988,14 @@ inline std::string getPrivateOperatorName(const std::string &OperatorToken) {
|
||||
}
|
||||
}
|
||||
|
||||
// Forwards to synthesizeCxxBasicMethod(), producing a thunk that calls a
|
||||
// virtual function.
|
||||
clang::CXXMethodDecl *synthesizeCxxVirtualMethod(
|
||||
swift::ClangImporter &Impl, const clang::CXXRecordDecl *derivedClass,
|
||||
const clang::CXXRecordDecl *baseClass, const clang::CXXMethodDecl *method);
|
||||
|
||||
// Exposed to produce a Swift method body for calling a Swift thunk.
|
||||
std::pair<swift::BraceStmt *, bool>
|
||||
synthesizeForwardingThunkBody(swift::AbstractFunctionDecl *afd, void *context);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -8,6 +8,12 @@ struct Base {
|
||||
virtual void foo() = 0;
|
||||
};
|
||||
|
||||
struct Base2 { virtual int f() = 0; };
|
||||
struct Base3 { virtual int f() { return 24; } };
|
||||
struct Derived2 : public Base2 { virtual int f() { return 42; } };
|
||||
struct Derived3 : public Base3 { virtual int f() { return 42; } };
|
||||
struct Derived4 : public Base3 { };
|
||||
|
||||
template <class T>
|
||||
struct Derived : Base {
|
||||
inline void foo() override {
|
||||
|
||||
@@ -8,6 +8,24 @@ import VirtualMethods
|
||||
var x = DerivedInt()
|
||||
x.callMe()
|
||||
|
||||
var b3 = Base3()
|
||||
var d2 = Derived2()
|
||||
var d3 = Derived3()
|
||||
var d4 = Derived4()
|
||||
|
||||
b3.f()
|
||||
d2.f()
|
||||
d3.f()
|
||||
d4.f()
|
||||
|
||||
// CHECK: invoke {{.*}} @_ZN5Base31fEv
|
||||
// CHECK: invoke {{.*}} @_ZN8Derived21fEv
|
||||
// CHECK: invoke {{.*}} @_ZN8Derived31fEv
|
||||
// CHECK: call swiftcc {{.*}} @"$sSo8Derived4V1fs5Int32VyF"
|
||||
|
||||
// CHECK: define {{.*}} @"$sSo8Derived4V1fs5Int32VyF"(ptr swiftself dereferenceable
|
||||
// CHECK: invoke {{.*}} @_ZN8Derived423__synthesizedBaseCall_fEv
|
||||
|
||||
// CHECK: define {{.*}}void @{{_ZN7DerivedIiE3fooEv|"\?foo@\?$Derived@H@@UEAAXXZ"}}
|
||||
// CHECK: call void @{{_Z21testFunctionCollectedv|"\?testFunctionCollected@@YAXXZ"}}
|
||||
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
|
||||
// CHECK: struct Base {
|
||||
// CHECK-NEXT: init()
|
||||
// CHECK-NEXT: @available(*, unavailable, message: "virtual functions are not yet available in Swift")
|
||||
// CHECK-NEXT: @available(*, unavailable, message: "virtual function is not available in Swift because it is pure")
|
||||
// CHECK-NEXT: mutating func foo()
|
||||
|
||||
// CHECK: struct Derived<CInt> {
|
||||
// CHECK: @available(*, unavailable, message: "virtual functions are not yet available in Swift")
|
||||
// CHECK: mutating func foo()
|
||||
// CHECK-NEXT: init()
|
||||
// CHECK-NEXT: mutating func foo()
|
||||
// CHECK: }
|
||||
|
||||
// CHECK: struct VirtualNonAbstractBase {
|
||||
// CHECK: @available(*, unavailable, message: "virtual functions are not yet available in Swift")
|
||||
// CHECK: func nonAbstractMethod()
|
||||
// CHECK-NEXT: init()
|
||||
// CHECK-NEXT: func nonAbstractMethod()
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
|
||||
import VirtualMethods
|
||||
|
||||
VirtualNonAbstractBase().nonAbstractMethod() // expected-error {{'nonAbstractMethod()' is unavailable: virtual functions are not yet available in Swift}}
|
||||
VirtualNonAbstractBase().nonAbstractMethod()
|
||||
|
||||
Reference in New Issue
Block a user