@@ -4768,24 +4768,63 @@ MemberRefExpr *getSelfInteropStaticCast(FuncDecl *funcDecl,
4768
4768
// to the base class during the invocation of the method.
4769
4769
static clang::CXXMethodDecl *synthesizeCxxBaseMethod (
4770
4770
ClangImporter &impl, const clang::CXXRecordDecl *derivedClass,
4771
- const clang::CXXRecordDecl *baseClass, const clang::CXXMethodDecl *method) {
4771
+ const clang::CXXRecordDecl *baseClass, const clang::CXXMethodDecl *method,
4772
+ bool promoteReferenceReturnToValue = false ,
4773
+ bool forceConstQualifier = false ) {
4772
4774
auto &clangCtx = impl.getClangASTContext ();
4773
4775
auto &clangSema = impl.getClangSema ();
4774
4776
4775
4777
// Create a new method in the derived class that calls the base method.
4776
- auto name = method->getNameInfo ().getName ();
4778
+ clang::DeclarationName name = method->getNameInfo ().getName ();
4777
4779
if (name.isIdentifier ()) {
4778
4780
std::string newName;
4779
4781
llvm::raw_string_ostream os (newName);
4780
4782
os << " __synthesizedBaseCall_" << name.getAsIdentifierInfo ()->getName ();
4781
4783
name = clang::DeclarationName (
4782
4784
&impl.getClangPreprocessor ().getIdentifierTable ().get (os.str ()));
4785
+ } else if (name.getCXXOverloadedOperator () == clang::OO_Subscript) {
4786
+ name = clang::DeclarationName (
4787
+ &impl.getClangPreprocessor ().getIdentifierTable ().get (
4788
+ " __synthesizedBaseCall_operatorSubscript" ));
4789
+ } else if (name.getCXXOverloadedOperator () == clang::OO_Star) {
4790
+ name = clang::DeclarationName (
4791
+ &impl.getClangPreprocessor ().getIdentifierTable ().get (
4792
+ " __synthesizedBaseCall_operatorStar" ));
4793
+ }
4794
+ auto methodType = method->getType ();
4795
+ // Check if we need to drop the reference from the return type
4796
+ // of the new method. This is needed when a synthesized `operator []`
4797
+ // derived-to-base call is invoked from Swift's subscript getter.
4798
+ if (promoteReferenceReturnToValue) {
4799
+ if (const auto *fpt = methodType->getAs <clang::FunctionProtoType>()) {
4800
+ auto retType = fpt->getReturnType ();
4801
+ if (retType->isReferenceType ()) {
4802
+ methodType = clangCtx.getFunctionType (retType->getPointeeType (),
4803
+ fpt->getParamTypes (),
4804
+ fpt->getExtProtoInfo ());
4805
+ }
4806
+ }
4807
+ }
4808
+ // Check if this method requires an additional `const` qualifier.
4809
+ // This might needed when a non-const synthesized `operator []`
4810
+ // derived-to-base call is invoked from Swift's subscript getter.
4811
+ bool castThisToNonConstThis = false ;
4812
+ if (forceConstQualifier) {
4813
+ if (const auto *fpt = methodType->getAs <clang::FunctionProtoType>()) {
4814
+ auto info = fpt->getExtProtoInfo ();
4815
+ if (!info.TypeQuals .hasConst ()) {
4816
+ info.TypeQuals .addConst ();
4817
+ castThisToNonConstThis = true ;
4818
+ methodType = clangCtx.getFunctionType (fpt->getReturnType (),
4819
+ fpt->getParamTypes (), info);
4820
+ }
4821
+ }
4783
4822
}
4784
4823
auto newMethod = clang::CXXMethodDecl::Create (
4785
4824
clangCtx, const_cast <clang::CXXRecordDecl *>(derivedClass),
4786
4825
method->getSourceRange ().getBegin (),
4787
- clang::DeclarationNameInfo (name, clang::SourceLocation ()),
4788
- method->getType (), method-> getTypeSourceInfo (), method->getStorageClass (),
4826
+ clang::DeclarationNameInfo (name, clang::SourceLocation ()), methodType,
4827
+ method->getTypeSourceInfo (), method->getStorageClass (),
4789
4828
method->UsesFPIntrin (), /* isInline=*/ true , method->getConstexprKind (),
4790
4829
method->getSourceRange ().getEnd ());
4791
4830
newMethod->setImplicit ();
@@ -4810,8 +4849,23 @@ static clang::CXXMethodDecl *synthesizeCxxBaseMethod(
4810
4849
auto diagState = clangSema.DelayedDiagnostics .push (diagPool);
4811
4850
4812
4851
// Construct the method's body.
4813
- auto *thisExpr = new (clangCtx) clang::CXXThisExpr (
4852
+ clang::Expr *thisExpr = new (clangCtx) clang::CXXThisExpr (
4814
4853
clang::SourceLocation (), newMethod->getThisType (), /* IsImplicit=*/ false );
4854
+ if (castThisToNonConstThis) {
4855
+ auto baseClassPtr =
4856
+ clangCtx.getPointerType (clangCtx.getRecordType (derivedClass));
4857
+ clang::CastKind Kind;
4858
+ clang::CXXCastPath Path;
4859
+ clangSema.CheckPointerConversion (thisExpr, baseClassPtr, Kind, Path,
4860
+ /* IgnoreBaseAccess=*/ false ,
4861
+ /* Diagnose=*/ true );
4862
+ auto conv = clangSema.ImpCastExprToType (thisExpr, baseClassPtr, Kind,
4863
+ clang::VK_PRValue, &Path);
4864
+ if (!conv.isUsable ())
4865
+ return nullptr ;
4866
+ thisExpr = conv.get ();
4867
+ }
4868
+
4815
4869
auto memberExpr = clangSema.BuildMemberExpr (
4816
4870
thisExpr, /* isArrow=*/ true , clang::SourceLocation (),
4817
4871
clang::NestedNameSpecifierLoc (), clang::SourceLocation (),
@@ -4865,7 +4919,13 @@ const clang::CXXMethodDecl *getCalledBaseCxxMethod(FuncDecl *baseMember) {
4865
4919
body->getElements ().front ().dyn_cast <Stmt *>());
4866
4920
if (!returnStmt)
4867
4921
return nullptr ;
4868
- auto *callExpr = dyn_cast<CallExpr>(returnStmt->getResult ());
4922
+ Expr *returnExpr = returnStmt->getResult ();
4923
+ // A member ref expr for `.pointee` access can be wrapping a call
4924
+ // when looking through the synthesized Swift body for `.pointee`
4925
+ // accessor.
4926
+ if (MemberRefExpr *mre = dyn_cast<MemberRefExpr>(returnExpr))
4927
+ returnExpr = mre->getBase ();
4928
+ auto *callExpr = dyn_cast<CallExpr>(returnExpr);
4869
4929
if (!callExpr)
4870
4930
return nullptr ;
4871
4931
auto *cv = callExpr->getCalledValue ();
@@ -4966,9 +5026,94 @@ synthesizeBaseClassMethodBody(AbstractFunctionDecl *afd, void *context) {
4966
5026
return {body, /* isTypeChecked=*/ true };
4967
5027
}
4968
5028
4969
- // Getters are relatively easy. Just cast and return the member:
4970
- // %0 = __swift_interopStaticCast<Base>(self)
4971
- // return %0.member
5029
+ // Synthesize a C++ method that returns the field of interest from the base
5030
+ // class. This lets Clang take care of the cast from the derived class
5031
+ // to the base class while the field is accessed.
5032
+ static clang::CXXMethodDecl *synthesizeCxxBaseGetterAccessorMethod (
5033
+ ClangImporter &impl, const clang::CXXRecordDecl *derivedClass,
5034
+ const clang::CXXRecordDecl *baseClass, const clang::FieldDecl *field) {
5035
+ auto &clangCtx = impl.getClangASTContext ();
5036
+ auto &clangSema = impl.getClangSema ();
5037
+
5038
+ // Create a new method in the derived class that calls the base method.
5039
+ auto name = field->getDeclName ();
5040
+ if (name.isIdentifier ()) {
5041
+ std::string newName;
5042
+ llvm::raw_string_ostream os (newName);
5043
+ os << " __synthesizedBaseGetterAccessor_"
5044
+ << name.getAsIdentifierInfo ()->getName ();
5045
+ name = clang::DeclarationName (
5046
+ &impl.getClangPreprocessor ().getIdentifierTable ().get (os.str ()));
5047
+ }
5048
+ auto returnType = field->getType ();
5049
+ if (returnType->isReferenceType ())
5050
+ returnType = returnType->getPointeeType ();
5051
+ clang::FunctionProtoType::ExtProtoInfo info;
5052
+ info.TypeQuals .addConst ();
5053
+ info.ExceptionSpec .Type = clang::EST_NoThrow;
5054
+ auto ftype = clangCtx.getFunctionType (returnType, {}, info);
5055
+ auto newMethod = clang::CXXMethodDecl::Create (
5056
+ clangCtx, const_cast <clang::CXXRecordDecl *>(derivedClass),
5057
+ field->getSourceRange ().getBegin (),
5058
+ clang::DeclarationNameInfo (name, clang::SourceLocation ()), ftype,
5059
+ clangCtx.getTrivialTypeSourceInfo (ftype), clang::SC_None,
5060
+ /* UsesFPIntrin=*/ false , /* isInline=*/ true ,
5061
+ clang::ConstexprSpecKind::Unspecified, field->getSourceRange ().getEnd ());
5062
+ newMethod->setImplicit ();
5063
+ newMethod->setImplicitlyInline ();
5064
+ newMethod->setAccess (clang::AccessSpecifier::AS_public);
5065
+
5066
+ // Create a new Clang diagnostic pool to capture any diagnostics
5067
+ // emitted during the construction of the method.
5068
+ clang::sema::DelayedDiagnosticPool diagPool{
5069
+ clangSema.DelayedDiagnostics .getCurrentPool ()};
5070
+ auto diagState = clangSema.DelayedDiagnostics .push (diagPool);
5071
+
5072
+ // Construct the method's body.
5073
+ auto *thisExpr = new (clangCtx) clang::CXXThisExpr (
5074
+ clang::SourceLocation (), newMethod->getThisType (), /* IsImplicit=*/ false );
5075
+ clang::QualType baseClassPtr = clangCtx.getRecordType (baseClass);
5076
+ baseClassPtr.addConst ();
5077
+ baseClassPtr = clangCtx.getPointerType (baseClassPtr);
5078
+
5079
+ clang::CastKind Kind;
5080
+ clang::CXXCastPath Path;
5081
+ clangSema.CheckPointerConversion (thisExpr, baseClassPtr, Kind, Path,
5082
+ /* IgnoreBaseAccess=*/ false ,
5083
+ /* Diagnose=*/ true );
5084
+ auto conv = clangSema.ImpCastExprToType (thisExpr, baseClassPtr, Kind,
5085
+ clang::VK_PRValue, &Path);
5086
+ if (!conv.isUsable ())
5087
+ return nullptr ;
5088
+ auto memberExpr = clangSema.BuildMemberExpr (
5089
+ conv.get (), /* isArrow=*/ true , clang::SourceLocation (),
5090
+ clang::NestedNameSpecifierLoc (), clang::SourceLocation (),
5091
+ const_cast <clang::FieldDecl *>(field),
5092
+ clang::DeclAccessPair::make (const_cast <clang::FieldDecl *>(field),
5093
+ clang::AS_public),
5094
+ /* HadMultipleCandidates=*/ false ,
5095
+ clang::DeclarationNameInfo (field->getDeclName (), clang::SourceLocation ()),
5096
+ returnType, clang::VK_LValue, clang::OK_Ordinary);
5097
+ auto returnCast = clangSema.ImpCastExprToType (
5098
+ memberExpr, returnType, clang::CK_LValueToRValue, clang::VK_PRValue);
5099
+ if (!returnCast.isUsable ())
5100
+ return nullptr ;
5101
+ auto returnStmt = clang::ReturnStmt::Create (clangCtx, clang::SourceLocation (),
5102
+ returnCast.get (), nullptr );
5103
+
5104
+ // Check if there were any Clang errors during the construction
5105
+ // of the method body.
5106
+ clangSema.DelayedDiagnostics .popWithoutEmitting (diagState);
5107
+ if (!diagPool.empty ())
5108
+ return nullptr ;
5109
+ newMethod->setBody (returnStmt);
5110
+ return newMethod;
5111
+ }
5112
+
5113
+ // Generates the body of a derived method, that invokes the base
5114
+ // field getter or the base subscript.
5115
+ // The method's body takes the following form:
5116
+ // return self.__synthesizedBaseCall_fn(args...)
4972
5117
static std::pair<BraceStmt *, bool >
4973
5118
synthesizeBaseClassFieldGetterBody (AbstractFunctionDecl *afd, void *context) {
4974
5119
ASTContext &ctx = afd->getASTContext ();
@@ -4980,48 +5125,80 @@ synthesizeBaseClassFieldGetterBody(AbstractFunctionDecl *afd, void *context) {
4980
5125
NominalTypeDecl *derivedStruct =
4981
5126
cast<NominalTypeDecl>(getterDecl->getDeclContext ()->getAsDecl ());
4982
5127
5128
+ const clang::Decl *baseClangDecl;
5129
+ if (baseClassVar->getClangDecl ())
5130
+ baseClangDecl = baseClassVar->getClangDecl ();
5131
+ else
5132
+ baseClangDecl =
5133
+ getCalledBaseCxxMethod (baseClassVar->getAccessor (AccessorKind::Get));
5134
+
5135
+ clang::CXXMethodDecl *baseGetterCxxMethod = nullptr ;
5136
+ if (auto *md = dyn_cast_or_null<clang::CXXMethodDecl>(baseClangDecl)) {
5137
+ // Subscript operator, or `.pointee` wrapper is represented through a
5138
+ // generated C++ method call that calls the base operator.
5139
+ baseGetterCxxMethod = synthesizeCxxBaseMethod (
5140
+ *static_cast <ClangImporter *>(ctx.getClangModuleLoader ()),
5141
+ cast<clang::CXXRecordDecl>(derivedStruct->getClangDecl ()),
5142
+ cast<clang::CXXRecordDecl>(baseStruct->getClangDecl ()), md,
5143
+ /* promoteReferenceReturnToValue=*/ true ,
5144
+ /* forceConstQualifier=*/ true );
5145
+ } else if (auto *fd = dyn_cast_or_null<clang::FieldDecl>(baseClangDecl)) {
5146
+ // Field getter is represented through a generated
5147
+ // C++ method call that returns the value of the base field.
5148
+ baseGetterCxxMethod = synthesizeCxxBaseGetterAccessorMethod (
5149
+ *static_cast <ClangImporter *>(ctx.getClangModuleLoader ()),
5150
+ cast<clang::CXXRecordDecl>(derivedStruct->getClangDecl ()),
5151
+ cast<clang::CXXRecordDecl>(baseStruct->getClangDecl ()), fd);
5152
+ }
5153
+
5154
+ if (!baseGetterCxxMethod) {
5155
+ ctx.Diags .diagnose (SourceLoc (), diag::failed_base_method_call_synthesis,
5156
+ getterDecl, baseStruct);
5157
+ auto body = BraceStmt::create (ctx, SourceLoc (), {}, SourceLoc (),
5158
+ /* implicit=*/ true );
5159
+ return {body, true };
5160
+ }
5161
+ auto *baseGetterMethod = cast<FuncDecl>(
5162
+ ctx.getClangModuleLoader ()->importDeclDirectly (baseGetterCxxMethod));
5163
+
4983
5164
auto selfDecl = getterDecl->getImplicitSelfDecl ();
4984
5165
auto selfExpr = new (ctx) DeclRefExpr (selfDecl, DeclNameLoc (),
4985
5166
/* implicit*/ true );
4986
5167
selfExpr->setType (selfDecl->getTypeInContext ());
4987
5168
4988
- auto staticCastRefExpr = getInteropStaticCastDeclRefExpr (
4989
- ctx, baseStruct->getClangDecl ()->getOwningModule (),
4990
- baseStruct->getSelfInterfaceType (),
4991
- derivedStruct->getSelfInterfaceType ());
5169
+ Argument selfArg = Argument::unlabeled (selfExpr);
4992
5170
4993
- auto *argList = ArgumentList::forImplicitUnlabeled (ctx, {selfExpr});
4994
- auto casted = CallExpr::createImplicit (ctx, staticCastRefExpr, argList);
4995
- casted->setType (baseStruct->getSelfInterfaceType ());
4996
- casted->setThrows (false );
5171
+ auto *baseMemberExpr =
5172
+ new (ctx) DeclRefExpr (ConcreteDeclRef (baseGetterMethod), DeclNameLoc (),
5173
+ /* Implicit=*/ true );
5174
+ baseMemberExpr->setType (baseGetterMethod->getInterfaceType ());
5175
+
5176
+ auto baseMemberDotCallExpr =
5177
+ DotSyntaxCallExpr::create (ctx, baseMemberExpr, SourceLoc (), selfArg);
5178
+ baseMemberDotCallExpr->setType (baseGetterMethod->getMethodInterfaceType ());
5179
+ baseMemberDotCallExpr->setThrows (false );
4997
5180
4998
- Expr *baseMember = nullptr ;
5181
+ ArgumentList *argumentList ;
4999
5182
if (auto subscript = dyn_cast<SubscriptDecl>(baseClassVar)) {
5000
5183
auto paramDecl = getterDecl->getParameters ()->get (0 );
5001
- auto paramRefExpr = new (ctx) DeclRefExpr (paramDecl,
5002
- DeclNameLoc (),
5003
- /* Implicit=*/ true );
5184
+ auto paramRefExpr = new (ctx) DeclRefExpr (paramDecl, DeclNameLoc (),
5185
+ /* Implicit=*/ true );
5004
5186
paramRefExpr->setType (paramDecl->getTypeInContext ());
5005
-
5006
- auto *argList = ArgumentList::forImplicitUnlabeled (ctx, {paramRefExpr});
5007
- baseMember = SubscriptExpr::create (ctx, casted, argList, subscript);
5008
- baseMember->setType (subscript->getElementInterfaceType ());
5187
+ argumentList = ArgumentList::forImplicitUnlabeled (ctx, {paramRefExpr});
5009
5188
} else {
5010
- // If the base class var has a clang decl, that means it's an access into a
5011
- // stored field. Otherwise, we're looking into another base class, so it's a
5012
- // another synthesized accessor.
5013
- AccessSemantics accessKind = baseClassVar->getClangDecl ()
5014
- ? AccessSemantics::DirectToStorage
5015
- : AccessSemantics::DirectToImplementation;
5016
- baseMember =
5017
- new (ctx) MemberRefExpr (casted, SourceLoc (), baseClassVar, DeclNameLoc (),
5018
- /* Implicit=*/ true , accessKind);
5019
- baseMember->setType (cast<VarDecl>(baseClassVar)->getTypeInContext ());
5189
+ argumentList = ArgumentList::forImplicitUnlabeled (ctx, {});
5020
5190
}
5021
5191
5022
- auto ret = new (ctx) ReturnStmt (SourceLoc (), baseMember);
5023
- auto body = BraceStmt::create (ctx, SourceLoc (), {ret}, SourceLoc (),
5024
- /* implicit*/ true );
5192
+ auto *baseMemberCallExpr =
5193
+ CallExpr::createImplicit (ctx, baseMemberDotCallExpr, argumentList);
5194
+ baseMemberCallExpr->setType (baseGetterMethod->getResultInterfaceType ());
5195
+ baseMemberCallExpr->setThrows (false );
5196
+
5197
+ auto returnStmt = new (ctx) ReturnStmt (SourceLoc (), baseMemberCallExpr,
5198
+ /* implicit=*/ true );
5199
+
5200
+ auto body = BraceStmt::create (ctx, SourceLoc (), {returnStmt}, SourceLoc (),
5201
+ /* implicit=*/ true );
5025
5202
return {body, /* isTypeChecked=*/ true };
5026
5203
}
5027
5204
0 commit comments