64
64
#include " clang/Parse/Parser.h"
65
65
#include " clang/Rewrite/Frontend/FrontendActions.h"
66
66
#include " clang/Rewrite/Frontend/Rewriters.h"
67
+ #include " clang/Sema/DelayedDiagnostic.h"
67
68
#include " clang/Sema/Lookup.h"
68
69
#include " clang/Sema/Sema.h"
69
70
#include " clang/Serialization/ASTReader.h"
@@ -4762,27 +4763,165 @@ MemberRefExpr *getSelfInteropStaticCast(FuncDecl *funcDecl,
4762
4763
return pointeePropertyRefExpr;
4763
4764
}
4764
4765
4765
- // For const methods generate the following:
4766
- // %0 = __swift_interopStaticCast<Base>(self)
4767
- // return %0.fn(args...)
4768
- // For mutating methods we have to pass self as a pointer:
4769
- // %0 = Builtin.addressof(&self)
4770
- // %1 = Builtin.reinterpretCast<UnsafeMutablePointer<Derived>>(%0)
4771
- // %2 = __swift_interopStaticCast<UnsafeMutablePointer<Base>?>(%1)
4772
- // %3 = %2!
4773
- // %4 = %3.pointee
4774
- // return %4.fn(args...)
4766
+ // Synthesize a C++ method that invokes the method from the base
4767
+ // class. This lets Clang take care of the cast from the derived class
4768
+ // to the base class during the invocation of the method.
4769
+ static clang::CXXMethodDecl *synthesizeCxxBaseMethod (
4770
+ ClangImporter &impl, const clang::CXXRecordDecl *derivedClass,
4771
+ const clang::CXXRecordDecl *baseClass, const clang::CXXMethodDecl *method) {
4772
+ auto &clangCtx = impl.getClangASTContext ();
4773
+ auto &clangSema = impl.getClangSema ();
4774
+
4775
+ // Create a new method in the derived class that calls the base method.
4776
+ auto name = method->getNameInfo ().getName ();
4777
+ if (name.isIdentifier ()) {
4778
+ std::string newName;
4779
+ llvm::raw_string_ostream os (newName);
4780
+ os << " __synthesizedBaseCall_" << name.getAsIdentifierInfo ()->getName ();
4781
+ name = clang::DeclarationName (
4782
+ &impl.getClangPreprocessor ().getIdentifierTable ().get (os.str ()));
4783
+ }
4784
+ auto newMethod = clang::CXXMethodDecl::Create (
4785
+ clangCtx, const_cast <clang::CXXRecordDecl *>(derivedClass),
4786
+ method->getSourceRange ().getBegin (),
4787
+ clang::DeclarationNameInfo (name, clang::SourceLocation ()),
4788
+ method->getType (), method->getTypeSourceInfo (), method->getStorageClass (),
4789
+ method->UsesFPIntrin (), /* isInline=*/ true , method->getConstexprKind (),
4790
+ method->getSourceRange ().getEnd ());
4791
+ newMethod->setImplicit ();
4792
+ newMethod->setImplicitlyInline ();
4793
+ newMethod->setAccess (clang::AccessSpecifier::AS_public);
4794
+
4795
+ llvm::SmallVector<clang::ParmVarDecl *, 4 > params;
4796
+ for (size_t i = 0 ; i < method->getNumParams (); ++i) {
4797
+ const auto ¶m = *method->getParamDecl (i);
4798
+ params.push_back (clang::ParmVarDecl::Create (
4799
+ clangCtx, newMethod, param.getSourceRange ().getBegin (),
4800
+ param.getLocation (), param.getIdentifier (), param.getType (),
4801
+ param.getTypeSourceInfo (), param.getStorageClass (),
4802
+ /* DefExpr=*/ nullptr ));
4803
+ }
4804
+ newMethod->setParams (params);
4805
+
4806
+ // Create a new Clang diagnostic pool to capture any diagnostics
4807
+ // emitted during the construction of the method.
4808
+ clang::sema::DelayedDiagnosticPool diagPool{
4809
+ clangSema.DelayedDiagnostics .getCurrentPool ()};
4810
+ auto diagState = clangSema.DelayedDiagnostics .push (diagPool);
4811
+
4812
+ // Construct the method's body.
4813
+ auto *thisExpr = new (clangCtx) clang::CXXThisExpr (
4814
+ clang::SourceLocation (), newMethod->getThisType (), /* IsImplicit=*/ false );
4815
+ auto memberExpr = clangSema.BuildMemberExpr (
4816
+ thisExpr, /* isArrow=*/ true , clang::SourceLocation (),
4817
+ clang::NestedNameSpecifierLoc (), clang::SourceLocation (),
4818
+ const_cast <clang::CXXMethodDecl *>(method),
4819
+ clang::DeclAccessPair::make (const_cast <clang::CXXMethodDecl *>(method),
4820
+ clang::AS_public),
4821
+ /* HadMultipleCandidates=*/ false , method->getNameInfo (),
4822
+ clangCtx.BoundMemberTy , clang::VK_PRValue, clang::OK_Ordinary);
4823
+ llvm::SmallVector<clang::Expr *, 4 > args;
4824
+ for (size_t i = 0 ; i < newMethod->getNumParams (); ++i) {
4825
+ auto *param = newMethod->getParamDecl (i);
4826
+ auto type = param->getType ();
4827
+ if (type->isReferenceType ())
4828
+ type = type->getPointeeType ();
4829
+ args.push_back (new (clangCtx) clang::DeclRefExpr (
4830
+ clangCtx, param, false , type, clang::ExprValueKind::VK_LValue,
4831
+ clang::SourceLocation ()));
4832
+ }
4833
+ auto memberCall = clangSema.BuildCallToMemberFunction (
4834
+ nullptr , memberExpr, clang::SourceLocation (), args,
4835
+ clang::SourceLocation ());
4836
+ if (!memberCall.isUsable ())
4837
+ return nullptr ;
4838
+ auto returnStmt = clang::ReturnStmt::Create (clangCtx, clang::SourceLocation (),
4839
+ memberCall.get (), nullptr );
4840
+
4841
+ // Check if there were any Clang errors during the construction
4842
+ // of the method body.
4843
+ clangSema.DelayedDiagnostics .popWithoutEmitting (diagState);
4844
+ if (!diagPool.empty ())
4845
+ return nullptr ;
4846
+
4847
+ newMethod->setBody (returnStmt);
4848
+ return newMethod;
4849
+ }
4850
+
4851
+ // Find the base C++ method called by the base function we want to synthesize
4852
+ // the derived thunk for.
4853
+ // The base C++ method is either the original C++ method that corresponds
4854
+ // to the imported base member, or it's the synthesized C++ method thunk
4855
+ // used in another synthesized derived thunk that acts as a base member here.
4856
+ const clang::CXXMethodDecl *getCalledBaseCxxMethod (FuncDecl *baseMember) {
4857
+ if (baseMember->getClangDecl ())
4858
+ return dyn_cast<clang::CXXMethodDecl>(baseMember->getClangDecl ());
4859
+ // Another synthesized derived thunk is used as a base member here,
4860
+ // so extract its synthesized C++ method.
4861
+ auto body = baseMember->getBody ();
4862
+ if (body->getElements ().empty ())
4863
+ return nullptr ;
4864
+ ReturnStmt *returnStmt = dyn_cast_or_null<ReturnStmt>(
4865
+ body->getElements ().front ().dyn_cast <Stmt *>());
4866
+ if (!returnStmt)
4867
+ return nullptr ;
4868
+ auto *callExpr = dyn_cast<CallExpr>(returnStmt->getResult ());
4869
+ if (!callExpr)
4870
+ return nullptr ;
4871
+ auto *cv = callExpr->getCalledValue ();
4872
+ if (!cv)
4873
+ return nullptr ;
4874
+ if (!cv->getClangDecl ())
4875
+ return nullptr ;
4876
+ return dyn_cast<clang::CXXMethodDecl>(cv->getClangDecl ());
4877
+ }
4878
+
4879
+ // Construct a Swift method that represents the synthesized C++ method
4880
+ // that invokes the base C++ method.
4881
+ FuncDecl *synthesizeBaseFunctionDeclCall (ClangImporter &impl, ASTContext &ctx,
4882
+ NominalTypeDecl *derivedStruct,
4883
+ NominalTypeDecl *baseStruct,
4884
+ FuncDecl *baseMember) {
4885
+ auto *cxxMethod = getCalledBaseCxxMethod (baseMember);
4886
+ if (!cxxMethod)
4887
+ return nullptr ;
4888
+ auto *newClangMethod = synthesizeCxxBaseMethod (
4889
+ impl, cast<clang::CXXRecordDecl>(derivedStruct->getClangDecl ()),
4890
+ cast<clang::CXXRecordDecl>(baseStruct->getClangDecl ()), cxxMethod);
4891
+ if (!newClangMethod)
4892
+ return nullptr ;
4893
+ return cast<FuncDecl>(
4894
+ ctx.getClangModuleLoader ()->importDeclDirectly (newClangMethod));
4895
+ }
4896
+
4897
+ // Generates the body of a derived method, that invokes the base
4898
+ // method.
4899
+ // The method's body takes the following form:
4900
+ // return self.__synthesizedBaseCall_fn(args...)
4775
4901
static std::pair<BraceStmt *, bool >
4776
4902
synthesizeBaseClassMethodBody (AbstractFunctionDecl *afd, void *context) {
4903
+
4777
4904
ASTContext &ctx = afd->getASTContext ();
4778
4905
4779
4906
auto funcDecl = cast<FuncDecl>(afd);
4780
4907
auto derivedStruct =
4781
4908
cast<NominalTypeDecl>(funcDecl->getDeclContext ()->getAsDecl ());
4782
4909
auto baseMember = static_cast <FuncDecl *>(context);
4783
- auto baseStruct = cast<NominalTypeDecl>(baseMember->getDeclContext ()->getAsDecl ());
4910
+ auto baseStruct =
4911
+ cast<NominalTypeDecl>(baseMember->getDeclContext ()->getAsDecl ());
4784
4912
auto baseType = baseStruct->getDeclaredType ();
4785
4913
4914
+ auto forwardedFunc = synthesizeBaseFunctionDeclCall (
4915
+ *static_cast <ClangImporter *>(ctx.getClangModuleLoader ()), ctx,
4916
+ derivedStruct, baseStruct, baseMember);
4917
+ if (!forwardedFunc) {
4918
+ ctx.Diags .diagnose (SourceLoc (), diag::failed_base_method_call_synthesis,
4919
+ funcDecl, baseStruct);
4920
+ auto body = BraceStmt::create (ctx, SourceLoc (), {}, SourceLoc (),
4921
+ /* implicit=*/ true );
4922
+ return {body, /* isTypeChecked=*/ true };
4923
+ }
4924
+
4786
4925
SmallVector<Expr *, 8 > forwardingParams;
4787
4926
for (auto param : *funcDecl->getParameters ()) {
4788
4927
auto paramRefExpr = new (ctx) DeclRefExpr (param, DeclNameLoc (),
@@ -4791,34 +4930,25 @@ synthesizeBaseClassMethodBody(AbstractFunctionDecl *afd, void *context) {
4791
4930
forwardingParams.push_back (paramRefExpr);
4792
4931
}
4793
4932
4794
- Argument casted = [&]() {
4795
- if (funcDecl->isMutating ()) {
4796
- return Argument::implicitInOut (
4797
- ctx, getSelfInteropStaticCast (funcDecl, baseStruct, derivedStruct));
4798
- }
4933
+ Argument selfArg = [&]() {
4799
4934
auto *selfDecl = funcDecl->getImplicitSelfDecl ();
4800
4935
auto selfExpr = new (ctx) DeclRefExpr (selfDecl, DeclNameLoc (),
4801
4936
/* implicit*/ true );
4937
+ if (funcDecl->isMutating ()) {
4938
+ selfExpr->setType (LValueType::get (selfDecl->getInterfaceType ()));
4939
+ return Argument::implicitInOut (ctx, selfExpr);
4940
+ }
4802
4941
selfExpr->setType (selfDecl->getTypeInContext ());
4803
-
4804
- auto staticCastRefExpr = getInteropStaticCastDeclRefExpr (
4805
- ctx, baseStruct->getClangDecl ()->getOwningModule (), baseType,
4806
- derivedStruct->getDeclaredType ());
4807
-
4808
- auto *argList = ArgumentList::forImplicitUnlabeled (ctx, {selfExpr});
4809
- auto castedCall = CallExpr::createImplicit (ctx, staticCastRefExpr, argList);
4810
- castedCall->setType (baseType);
4811
- castedCall->setThrows (false );
4812
- return Argument::unlabeled (castedCall);
4942
+ return Argument::unlabeled (selfExpr);
4813
4943
}();
4814
4944
4815
4945
auto *baseMemberExpr =
4816
- new (ctx) DeclRefExpr (ConcreteDeclRef (baseMember ), DeclNameLoc (),
4946
+ new (ctx) DeclRefExpr (ConcreteDeclRef (forwardedFunc ), DeclNameLoc (),
4817
4947
/* Implicit=*/ true );
4818
- baseMemberExpr->setType (baseMember ->getInterfaceType ());
4948
+ baseMemberExpr->setType (forwardedFunc ->getInterfaceType ());
4819
4949
4820
4950
auto baseMemberDotCallExpr =
4821
- DotSyntaxCallExpr::create (ctx, baseMemberExpr, SourceLoc (), casted );
4951
+ DotSyntaxCallExpr::create (ctx, baseMemberExpr, SourceLoc (), selfArg );
4822
4952
baseMemberDotCallExpr->setType (baseMember->getMethodInterfaceType ());
4823
4953
baseMemberDotCallExpr->setThrows (false );
4824
4954
0 commit comments