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
|
ReferenceReturnTypeBehaviorForBaseMethodSynthesis
|
||||||
referenceReturnTypeBehavior =
|
referenceReturnTypeBehavior =
|
||||||
ReferenceReturnTypeBehaviorForBaseMethodSynthesis::KeepReference,
|
ReferenceReturnTypeBehaviorForBaseMethodSynthesis::KeepReference,
|
||||||
bool forceConstQualifier = false) {
|
bool forceConstQualifier = false,
|
||||||
|
bool isVirtualCall = false) {
|
||||||
auto &clangCtx = impl.getClangASTContext();
|
auto &clangCtx = impl.getClangASTContext();
|
||||||
auto &clangSema = impl.getClangSema();
|
auto &clangSema = impl.getClangSema();
|
||||||
|
|
||||||
@@ -4794,17 +4795,21 @@ static clang::CXXMethodDecl *synthesizeCxxBaseMethod(
|
|||||||
if (name.isIdentifier()) {
|
if (name.isIdentifier()) {
|
||||||
std::string newName;
|
std::string newName;
|
||||||
llvm::raw_string_ostream os(newName);
|
llvm::raw_string_ostream os(newName);
|
||||||
os << "__synthesizedBaseCall_" << name.getAsIdentifierInfo()->getName();
|
os << (isVirtualCall ? "__synthesizedVirtualCall_" :
|
||||||
|
"__synthesizedBaseCall_")
|
||||||
|
<< name.getAsIdentifierInfo()->getName();
|
||||||
name = clang::DeclarationName(
|
name = clang::DeclarationName(
|
||||||
&impl.getClangPreprocessor().getIdentifierTable().get(os.str()));
|
&impl.getClangPreprocessor().getIdentifierTable().get(os.str()));
|
||||||
} else if (name.getCXXOverloadedOperator() == clang::OO_Subscript) {
|
} else if (name.getCXXOverloadedOperator() == clang::OO_Subscript) {
|
||||||
name = clang::DeclarationName(
|
name = clang::DeclarationName(
|
||||||
&impl.getClangPreprocessor().getIdentifierTable().get(
|
&impl.getClangPreprocessor().getIdentifierTable().get(
|
||||||
"__synthesizedBaseCall_operatorSubscript"));
|
(isVirtualCall ? "__synthesizedVirtualCall_operatorSubscript" :
|
||||||
|
"__synthesizedBaseCall_operatorSubscript")));
|
||||||
} else if (name.getCXXOverloadedOperator() == clang::OO_Star) {
|
} else if (name.getCXXOverloadedOperator() == clang::OO_Star) {
|
||||||
name = clang::DeclarationName(
|
name = clang::DeclarationName(
|
||||||
&impl.getClangPreprocessor().getIdentifierTable().get(
|
&impl.getClangPreprocessor().getIdentifierTable().get(
|
||||||
"__synthesizedBaseCall_operatorStar"));
|
(isVirtualCall ? "__synthesizedVirtualCall_operatorStar" :
|
||||||
|
"__synthesizedBaseCall_operatorStar")));
|
||||||
}
|
}
|
||||||
auto methodType = method->getType();
|
auto methodType = method->getType();
|
||||||
// Check if we need to drop the reference from the return type
|
// Check if we need to drop the reference from the return type
|
||||||
@@ -4930,6 +4935,16 @@ static clang::CXXMethodDecl *synthesizeCxxBaseMethod(
|
|||||||
return newMethod;
|
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
|
// Find the base C++ method called by the base function we want to synthesize
|
||||||
// the derived thunk for.
|
// the derived thunk for.
|
||||||
// The base C++ method is either the original C++ method that corresponds
|
// 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
|
// 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
|
// metatype arguments to aid in typechecking, but they shouldn't be forwarded to
|
||||||
// the corresponding C++ function.
|
// the corresponding C++ function.
|
||||||
static std::pair<BraceStmt *, bool>
|
std::pair<BraceStmt *, bool>
|
||||||
synthesizeForwardingThunkBody(AbstractFunctionDecl *afd, void *context) {
|
synthesizeForwardingThunkBody(AbstractFunctionDecl *afd, void *context) {
|
||||||
ASTContext &ctx = afd->getASTContext();
|
ASTContext &ctx = afd->getASTContext();
|
||||||
|
|
||||||
|
|||||||
@@ -3711,9 +3711,32 @@ namespace {
|
|||||||
Decl *VisitCXXMethodDecl(const clang::CXXMethodDecl *decl) {
|
Decl *VisitCXXMethodDecl(const clang::CXXMethodDecl *decl) {
|
||||||
auto method = VisitFunctionDecl(decl);
|
auto method = VisitFunctionDecl(decl);
|
||||||
if (decl->isVirtual() && isa_and_nonnull<ValueDecl>(method)) {
|
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(
|
Impl.markUnavailable(
|
||||||
cast<ValueDecl>(method),
|
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 ||
|
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
|
#endif
|
||||||
|
|||||||
@@ -8,6 +8,12 @@ struct Base {
|
|||||||
virtual void foo() = 0;
|
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>
|
template <class T>
|
||||||
struct Derived : Base {
|
struct Derived : Base {
|
||||||
inline void foo() override {
|
inline void foo() override {
|
||||||
|
|||||||
@@ -8,6 +8,24 @@ import VirtualMethods
|
|||||||
var x = DerivedInt()
|
var x = DerivedInt()
|
||||||
x.callMe()
|
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: define {{.*}}void @{{_ZN7DerivedIiE3fooEv|"\?foo@\?$Derived@H@@UEAAXXZ"}}
|
||||||
// CHECK: call void @{{_Z21testFunctionCollectedv|"\?testFunctionCollected@@YAXXZ"}}
|
// CHECK: call void @{{_Z21testFunctionCollectedv|"\?testFunctionCollected@@YAXXZ"}}
|
||||||
|
|
||||||
|
|||||||
@@ -2,14 +2,14 @@
|
|||||||
|
|
||||||
// CHECK: struct Base {
|
// CHECK: struct Base {
|
||||||
// CHECK-NEXT: init()
|
// 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-NEXT: mutating func foo()
|
||||||
|
|
||||||
// CHECK: struct Derived<CInt> {
|
// CHECK: struct Derived<CInt> {
|
||||||
// CHECK: @available(*, unavailable, message: "virtual functions are not yet available in Swift")
|
// CHECK-NEXT: init()
|
||||||
// CHECK: mutating func foo()
|
// CHECK-NEXT: mutating func foo()
|
||||||
// CHECK: }
|
// CHECK: }
|
||||||
|
|
||||||
// CHECK: struct VirtualNonAbstractBase {
|
// CHECK: struct VirtualNonAbstractBase {
|
||||||
// CHECK: @available(*, unavailable, message: "virtual functions are not yet available in Swift")
|
// CHECK-NEXT: init()
|
||||||
// CHECK: func nonAbstractMethod()
|
// CHECK-NEXT: func nonAbstractMethod()
|
||||||
|
|||||||
@@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
import VirtualMethods
|
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