diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 5af4c08f64cd8..768497712ecd6 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -96,6 +96,11 @@ C++ Language Changes asm((std::string_view("nop")) ::: (std::string_view("memory"))); } +- Implemented `GCC bound member functions extension `_ for Itanium ABI. + This extension allows extracting the function pointer from a bound pointer to member function. + It is useful to save vtable lookups when the same member function is executed multiple times inside a loop. + When using this extension, a warning is emitted unless ``-Wno-pmf-conversions`` is passed. + C++2c Feature Support ^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/AST/OperationKinds.def b/clang/include/clang/AST/OperationKinds.def index 790dd572a7c99..489d89a697dc3 100644 --- a/clang/include/clang/AST/OperationKinds.def +++ b/clang/include/clang/AST/OperationKinds.def @@ -152,6 +152,10 @@ CAST_OPERATION(MemberPointerToBoolean) /// many ABIs do not guarantee this on all possible intermediate types). CAST_OPERATION(ReinterpretMemberPointer) +/// CK_BoundPointerToMemberFunctionToFunctionPointer - Convert a bound +/// member function pointer to a function pointer. This is a GNU extension. +CAST_OPERATION(BoundMemberFunctionToFunctionPointer) + /// CK_UserDefinedConversion - Conversion using a user defined type /// conversion function. /// struct A { operator int(); }; int i = int(A()); diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index d97bbfee2e4d5..8e5a4cba87c95 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -795,6 +795,7 @@ def DuplicateDeclSpecifier : DiagGroup<"duplicate-decl-specifier">; def CompareDistinctPointerType : DiagGroup<"compare-distinct-pointer-types">; def GNUUnionCast : DiagGroup<"gnu-union-cast">; def GNUVariableSizedTypeNotAtEnd : DiagGroup<"gnu-variable-sized-type-not-at-end">; +def GNUPMFCast : DiagGroup<"pmf-conversions">; def Varargs : DiagGroup<"varargs">; def XorUsedAsPow : DiagGroup<"xor-used-as-pow">; @@ -1294,22 +1295,21 @@ def C2y : DiagGroup<"c2y-extensions">; def GNUBinaryLiteral : DiagGroup<"gnu-binary-literal">; // A warning group for warnings about GCC extensions. -def GNU : DiagGroup<"gnu", [GNUAlignofExpression, GNUAnonymousStruct, - GNUAutoType, GNUBinaryLiteral, GNUCaseRange, - GNUComplexInteger, GNUCompoundLiteralInitializer, - GNUConditionalOmittedOperand, GNUDesignator, - GNUEmptyStruct, - VLAExtension, GNUFlexibleArrayInitializer, - GNUFlexibleArrayUnionMember, GNUFoldingConstant, - GNUImaginaryConstant, GNUIncludeNext, - GNULabelsAsValue, GNULineMarker, GNUNullPointerArithmetic, - GNUOffsetofExtensions, GNUPointerArith, - RedeclaredClassMember, GNURedeclaredEnum, - GNUStatementExpression, GNUStaticFloatInit, - GNUStringLiteralOperatorTemplate, GNUUnionCast, - GNUVariableSizedTypeNotAtEnd, ZeroLengthArray, - GNUZeroLineDirective, - GNUZeroVariadicMacroArguments]>; +def GNU + : DiagGroup< + "gnu", [GNUAlignofExpression, GNUAnonymousStruct, GNUAutoType, + GNUBinaryLiteral, GNUCaseRange, GNUComplexInteger, + GNUCompoundLiteralInitializer, GNUConditionalOmittedOperand, + GNUDesignator, GNUEmptyStruct, VLAExtension, + GNUFlexibleArrayInitializer, GNUFlexibleArrayUnionMember, + GNUFoldingConstant, GNUImaginaryConstant, GNUIncludeNext, + GNULabelsAsValue, GNULineMarker, GNUNullPointerArithmetic, + GNUOffsetofExtensions, GNUPointerArith, RedeclaredClassMember, + GNURedeclaredEnum, GNUStatementExpression, GNUStaticFloatInit, + GNUStringLiteralOperatorTemplate, GNUUnionCast, + GNUVariableSizedTypeNotAtEnd, ZeroLengthArray, + GNUZeroLineDirective, GNUZeroVariadicMacroArguments, + GNUPMFCast]>; // A warning group for warnings about code that clang accepts but gcc doesn't. def GccCompat : DiagGroup<"gcc-compat">; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 3f7499d8656bd..5c882df7fcbdf 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -5144,6 +5144,10 @@ def err_ovl_unresolvable : Error< def err_bound_member_function : Error< "reference to non-static member function must be called" "%select{|; did you mean to call it with no arguments?}0">; +def ext_bound_member_function_conversion + : ExtWarn<"converting the bound member function %1 to a function pointer " + "%2 is a GNU extension">, + InGroup; def note_possible_target_of_call : Note<"possible target for call">; def err_no_viable_destructor : Error< "no viable destructor found for class %0">; diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 0d3c2065cd58c..7dc299827916b 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -109,6 +109,7 @@ def CK_ArrayToPointerDecay : I32EnumAttrCase<"array_to_ptrdecay", 11>; // CK_DerivedToBaseMemberPointer def CK_MemberPointerToBoolean : I32EnumAttrCase<"member_ptr_to_bool", 17>; // CK_ReinterpretMemberPointer +// CK_BoundMemberFunctionToFunctionPointer // CK_UserDefinedConversion // CK_ConstructorConversion def CK_IntegralToPointer : I32EnumAttrCase<"int_to_ptr", 21>; diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 59c0e47c7c195..e42d626acacb6 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -1864,6 +1864,13 @@ bool CastExpr::CastConsistency() const { assert(getSubExpr()->getType()->isMemberPointerType()); goto CheckNoBasePath; + case CK_BoundMemberFunctionToFunctionPointer: + assert(getType()->isFunctionPointerType()); + assert(getSubExpr()->getType()->isMemberPointerType() || + getSubExpr()->getType()->isSpecificPlaceholderType( + BuiltinType::BoundMember)); + goto CheckNoBasePath; + case CK_BitCast: // Arbitrary casts to C pointer types count as bitcasts. // Otherwise, we should only have block and ObjC pointer casts diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index d1cc722fb7945..b104a4e9416c0 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -15103,6 +15103,7 @@ bool IntExprEvaluator::VisitCastExpr(const CastExpr *E) { case CK_BaseToDerivedMemberPointer: case CK_DerivedToBaseMemberPointer: case CK_ReinterpretMemberPointer: + case CK_BoundMemberFunctionToFunctionPointer: case CK_ConstructorConversion: case CK_IntegralToPointer: case CK_ToVoid: @@ -15960,6 +15961,7 @@ bool ComplexExprEvaluator::VisitCastExpr(const CastExpr *E) { case CK_DerivedToBaseMemberPointer: case CK_MemberPointerToBoolean: case CK_ReinterpretMemberPointer: + case CK_BoundMemberFunctionToFunctionPointer: case CK_ConstructorConversion: case CK_IntegralToPointer: case CK_PointerToIntegral: diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index cffe5c5cd1ec3..1c4a06ad8d61d 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -83,6 +83,7 @@ Address CIRGenFunction::emitPointerWithAlignment(const Expr *expr, case CK_NullToMemberPointer: case CK_NullToPointer: case CK_ReinterpretMemberPointer: + case CK_BoundMemberFunctionToFunctionPointer: // Common pointer conversions, nothing to do here. // TODO: Is there any reason to treat base-to-derived conversions // specially? diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index abb88477062fc..bf22a62d20c11 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -5417,6 +5417,7 @@ LValue CodeGenFunction::EmitCastLValue(const CastExpr *E) { case CK_BaseToDerivedMemberPointer: case CK_MemberPointerToBoolean: case CK_ReinterpretMemberPointer: + case CK_BoundMemberFunctionToFunctionPointer: case CK_AnyPointerToBlockPointerCast: case CK_ARCProduceObject: case CK_ARCConsumeObject: diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp index 87b2a73fb0c03..91fdf8a072111 100644 --- a/clang/lib/CodeGen/CGExprAgg.cpp +++ b/clang/lib/CodeGen/CGExprAgg.cpp @@ -1043,6 +1043,7 @@ void AggExprEmitter::VisitCastExpr(CastExpr *E) { case CK_DerivedToBaseMemberPointer: case CK_MemberPointerToBoolean: case CK_ReinterpretMemberPointer: + case CK_BoundMemberFunctionToFunctionPointer: case CK_IntegralToPointer: case CK_PointerToIntegral: case CK_PointerToBoolean: @@ -1600,6 +1601,7 @@ static bool castPreservesZero(const CastExpr *CE) { case CK_MemberPointerToBoolean: case CK_NullToMemberPointer: case CK_ReinterpretMemberPointer: + case CK_BoundMemberFunctionToFunctionPointer: // FIXME: ABI-dependent. return false; diff --git a/clang/lib/CodeGen/CGExprComplex.cpp b/clang/lib/CodeGen/CGExprComplex.cpp index f556594f4a9ec..008082a493a2a 100644 --- a/clang/lib/CodeGen/CGExprComplex.cpp +++ b/clang/lib/CodeGen/CGExprComplex.cpp @@ -575,6 +575,7 @@ ComplexPairTy ComplexExprEmitter::EmitCast(CastKind CK, Expr *Op, case CK_DerivedToBaseMemberPointer: case CK_MemberPointerToBoolean: case CK_ReinterpretMemberPointer: + case CK_BoundMemberFunctionToFunctionPointer: case CK_ConstructorConversion: case CK_IntegralToPointer: case CK_PointerToIntegral: diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp index b21ebeee4bed1..ca3a2cd9af3dd 100644 --- a/clang/lib/CodeGen/CGExprConstant.cpp +++ b/clang/lib/CodeGen/CGExprConstant.cpp @@ -1272,6 +1272,7 @@ class ConstExprEmitter llvm_unreachable("builtin functions are handled elsewhere"); case CK_ReinterpretMemberPointer: + case CK_BoundMemberFunctionToFunctionPointer: case CK_DerivedToBaseMemberPointer: case CK_BaseToDerivedMemberPointer: { auto C = Emitter.tryEmitPrivate(subExpr, subExpr->getType()); diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index 8dbbcdaef25d8..fa64df8be8c27 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -2628,6 +2628,38 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) { return CGF.CGM.getCXXABI().EmitNullMemberPointer(MPT); } + case CK_BoundMemberFunctionToFunctionPointer: { + // Special handling bound member functions + if (E->isBoundMemberFunction(CGF.getContext())) { + auto *BO = cast(E->IgnoreParens()); + const Expr *BaseExpr = BO->getLHS(); + const Expr *MemFnExpr = BO->getRHS(); + + const auto *MPT = MemFnExpr->getType()->castAs(); + const auto *FPT = MPT->getPointeeType()->castAs(); + const auto *RD = MPT->getMostRecentCXXRecordDecl(); + + // Emit the 'this' pointer. + Address This = Address::invalid(); + if (BO->getOpcode() == BO_PtrMemI) + This = CGF.EmitPointerWithAlignment(BaseExpr, nullptr, nullptr, + KnownNonNull); + else + This = CGF.EmitLValue(BaseExpr, KnownNonNull).getAddress(); + + // Get the member function pointer. + llvm::Value *MemFnPtr = CGF.EmitScalarExpr(MemFnExpr); + + // Ask the ABI to load the callee. Note that This is modified. + llvm::Value *ThisPtrForCall = nullptr; + CGCallee Callee = CGF.CGM.getCXXABI().EmitLoadOfMemberFunctionPointer( + CGF, BO, This, ThisPtrForCall, MemFnPtr, MPT); + return Callee.getFunctionPointer(); + } + + // fallback to the case without the base object address + } + [[fallthrough]]; case CK_ReinterpretMemberPointer: case CK_BaseToDerivedMemberPointer: case CK_DerivedToBaseMemberPointer: { diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp index 70b53be7e77a3..6e4732ea977a9 100644 --- a/clang/lib/CodeGen/ItaniumCXXABI.cpp +++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -926,7 +926,8 @@ ItaniumCXXABI::EmitMemberPointerConversion(CodeGenFunction &CGF, assert(E->getCastKind() == CK_DerivedToBaseMemberPointer || E->getCastKind() == CK_BaseToDerivedMemberPointer || - E->getCastKind() == CK_ReinterpretMemberPointer); + E->getCastKind() == CK_ReinterpretMemberPointer || + E->getCastKind() == CK_BoundMemberFunctionToFunctionPointer); CGBuilderTy &Builder = CGF.Builder; QualType DstType = E->getType(); @@ -973,6 +974,8 @@ ItaniumCXXABI::EmitMemberPointerConversion(CodeGenFunction &CGF, // Under Itanium, reinterprets don't require any additional processing. if (E->getCastKind() == CK_ReinterpretMemberPointer) return src; + if (E->getCastKind() == CK_BoundMemberFunctionToFunctionPointer) + return Builder.CreateExtractValue(src, 0, "src.ptr"); llvm::Constant *adj = getMemberPointerAdjustment(E); if (!adj) return src; @@ -1047,7 +1050,8 @@ ItaniumCXXABI::EmitMemberPointerConversion(const CastExpr *E, llvm::Constant *src) { assert(E->getCastKind() == CK_DerivedToBaseMemberPointer || E->getCastKind() == CK_BaseToDerivedMemberPointer || - E->getCastKind() == CK_ReinterpretMemberPointer); + E->getCastKind() == CK_ReinterpretMemberPointer || + E->getCastKind() == CK_BoundMemberFunctionToFunctionPointer); QualType DstType = E->getType(); @@ -1057,6 +1061,20 @@ ItaniumCXXABI::EmitMemberPointerConversion(const CastExpr *E, // Under Itanium, reinterprets don't require any additional processing. if (E->getCastKind() == CK_ReinterpretMemberPointer) return src; + if (E->getCastKind() == CK_BoundMemberFunctionToFunctionPointer) { + llvm::Type *PtrTy = llvm::PointerType::getUnqual(CGM.getLLVMContext()); + llvm::Constant *FuncPtr = llvm::ConstantExpr::getIntToPtr( + ConstantFoldExtractValueInstruction(src, 0), PtrTy); + + const auto &NewAuthInfo = CGM.getFunctionPointerAuthInfo(DstType); + const auto &CurAuthInfo = + CGM.getMemberFunctionPointerAuthInfo(E->getSubExpr()->getType()); + + if (!NewAuthInfo && !CurAuthInfo) + return FuncPtr; + + return pointerAuthResignConstant(FuncPtr, CurAuthInfo, NewAuthInfo, CGM); + } // If the adjustment is trivial, we don't need to do anything. llvm::Constant *adj = getMemberPointerAdjustment(E); diff --git a/clang/lib/Edit/RewriteObjCFoundationAPI.cpp b/clang/lib/Edit/RewriteObjCFoundationAPI.cpp index 627a1d6fb3dd5..51f1de0edae4b 100644 --- a/clang/lib/Edit/RewriteObjCFoundationAPI.cpp +++ b/clang/lib/Edit/RewriteObjCFoundationAPI.cpp @@ -1054,6 +1054,7 @@ static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg, case CK_DerivedToBaseMemberPointer: case CK_MemberPointerToBoolean: case CK_ReinterpretMemberPointer: + case CK_BoundMemberFunctionToFunctionPointer: case CK_ConstructorConversion: case CK_IntegralToPointer: case CK_PointerToIntegral: diff --git a/clang/lib/Sema/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp index 14e16bc39eb3a..fe1e485cc92a6 100644 --- a/clang/lib/Sema/SemaCast.cpp +++ b/clang/lib/Sema/SemaCast.cpp @@ -153,6 +153,11 @@ namespace { bool isPlaceholder(BuiltinType::Kind K) const { return PlaceholderKind == K; } + bool isBoundPMFConversion() const { + return isPlaceholder(BuiltinType::BoundMember) && + DestType->isFunctionPointerType() && + SrcExpr.get()->isBoundMemberFunction(Self.Context); + } // Language specific cast restrictions for address spaces. void checkAddressSpaceCast(QualType SrcType, QualType DestType); @@ -254,13 +259,11 @@ static TryCastResult TryStaticDowncast(Sema &Self, CanQualType SrcType, QualType OrigDestType, unsigned &msg, CastKind &Kind, CXXCastPath &BasePath); -static TryCastResult TryStaticMemberPointerUpcast(Sema &Self, ExprResult &SrcExpr, - QualType SrcType, - QualType DestType,bool CStyle, - SourceRange OpRange, - unsigned &msg, - CastKind &Kind, - CXXCastPath &BasePath); +static TryCastResult +TryStaticMemberPointerUpcast(Sema &Self, ExprResult &SrcExpr, QualType SrcType, + QualType DestType, bool CStyle, + SourceRange OpRange, unsigned &msg, CastKind &Kind, + CXXCastPath &BasePath); static TryCastResult TryStaticImplicitCast(Sema &Self, ExprResult &SrcExpr, QualType DestType, @@ -1234,9 +1237,10 @@ static unsigned int checkCastFunctionType(Sema &Self, const ExprResult &SrcExpr, /// like this: /// char *bytes = reinterpret_cast\(int_ptr); void CastOperation::CheckReinterpretCast() { - if (ValueKind == VK_PRValue && !isPlaceholder(BuiltinType::Overload)) - SrcExpr = Self.DefaultFunctionArrayLvalueConversion(SrcExpr.get()); - else + if (ValueKind == VK_PRValue) { + if (!isPlaceholder(BuiltinType::Overload) && !isBoundPMFConversion()) + SrcExpr = Self.DefaultFunctionArrayLvalueConversion(SrcExpr.get()); + } else checkNonOverloadPlaceholders(); if (SrcExpr.isInvalid()) // if conversion failed, don't report another error return; @@ -2363,6 +2367,16 @@ static TryCastResult TryReinterpretCast(Sema &Self, ExprResult &SrcExpr, return TC_Success; } + // GNU extension: check if we can convert a pmf to a function pointer + if (DestType->isFunctionPointerType() && + (SrcType->isMemberFunctionPointerType() || + SrcExpr.get()->isBoundMemberFunction(Self.Context)) && + Self.Context.getTargetInfo().getCXXABI().isItaniumFamily()) { + Kind = CK_BoundMemberFunctionToFunctionPointer; + msg = diag::ext_bound_member_function_conversion; + return TC_Extension; + } + // See below for the enumeral issue. if (SrcType->isNullPtrType() && DestType->isIntegralType(Self.Context)) { // C++0x 5.2.10p4: A pointer can be explicitly converted to any integral @@ -2729,9 +2743,11 @@ void CastOperation::CheckCXXCStyleCast(bool FunctionalStyle, return; } - checkNonOverloadPlaceholders(); - if (SrcExpr.isInvalid()) - return; + if (!isBoundPMFConversion()) { + checkNonOverloadPlaceholders(); + if (SrcExpr.isInvalid()) + return; + } } // C++ 5.2.9p4: Any expression can be explicitly converted to type "cv void". @@ -2769,7 +2785,7 @@ void CastOperation::CheckCXXCStyleCast(bool FunctionalStyle, } if (ValueKind == VK_PRValue && !DestType->isRecordType() && - !isPlaceholder(BuiltinType::Overload)) { + !isPlaceholder(BuiltinType::Overload) && !isBoundPMFConversion()) { SrcExpr = Self.DefaultFunctionArrayLvalueConversion(SrcExpr.get()); if (SrcExpr.isInvalid()) return; @@ -2827,12 +2843,16 @@ void CastOperation::CheckCXXCStyleCast(bool FunctionalStyle, return; if (tcr == TC_NotApplicable) { - // ... or if that is not possible, a static_cast, ignoring const and - // addr space, ... - tcr = TryStaticCast(Self, SrcExpr, DestType, CCK, OpRange, msg, Kind, - BasePath, ListInitialization); - if (SrcExpr.isInvalid()) - return; + // FIXME: Bound member function to function pointer conversion is blocked + // by an immediate error inside ``Sema::CheckPlaceholderExpr``. + if (!isBoundPMFConversion()) { + // ... or if that is not possible, a static_cast, ignoring const and + // addr space, ... + tcr = TryStaticCast(Self, SrcExpr, DestType, CCK, OpRange, msg, Kind, + BasePath, ListInitialization); + if (SrcExpr.isInvalid()) + return; + } if (tcr == TC_NotApplicable) { // ... and finally a reinterpret_cast, ignoring const and addr space. diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp index 3d0a69a515ab8..d28752febce43 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -524,7 +524,8 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, case CK_VectorSplat: case CK_HLSLElementwiseCast: case CK_HLSLAggregateSplatCast: - case CK_HLSLVectorTruncation: { + case CK_HLSLVectorTruncation: + case CK_BoundMemberFunctionToFunctionPointer: { QualType resultType = CastE->getType(); if (CastE->isGLValue()) resultType = getContext().getPointerType(resultType); diff --git a/clang/test/CodeGenCXX/pmf-conversions.cpp b/clang/test/CodeGenCXX/pmf-conversions.cpp new file mode 100644 index 0000000000000..c224870b19ff9 --- /dev/null +++ b/clang/test/CodeGenCXX/pmf-conversions.cpp @@ -0,0 +1,105 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --check-globals all --version 5 +// RUN: %clang_cc1 -triple x86_64-linux-gnu -Wno-pmf-conversions %s -O3 -emit-llvm -o - | FileCheck %s + +struct A { + int data; +//. +// CHECK: @method = local_unnamed_addr global ptr @_ZN1A6methodEv, align 8 +//. +// CHECK-LABEL: define linkonce_odr noundef i32 @_ZN1A6methodEv( +// CHECK-SAME: ptr noundef nonnull align 8 dereferenceable(12) [[THIS:%.*]]) #[[ATTR0:[0-9]+]] comdat align 2 { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret i32 0 +// + int method() { return 0; } + virtual int virtual_method() { return 1; } + virtual ~A() = default; +}; + +struct C { + int data; +}; + +struct B : C, A { + virtual int virtual_method() override { return 2; } +}; + +using pmf_type = int (A::*)(); +using pf_type = int (*)(A*); + +pf_type method = reinterpret_cast(&A::method); + +// CHECK-LABEL: define dso_local noundef ptr @_Z11convert_pmfP1AMS_FivE( +// CHECK-SAME: ptr noundef readonly captures(none) [[P:%.*]], i64 [[METHOD_COERCE0:%.*]], i64 [[METHOD_COERCE1:%.*]]) local_unnamed_addr #[[ATTR1:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[TMP0:%.*]] = and i64 [[METHOD_COERCE0]], 1 +// CHECK-NEXT: [[MEMPTR_ISVIRTUAL_NOT:%.*]] = icmp eq i64 [[TMP0]], 0 +// CHECK-NEXT: br i1 [[MEMPTR_ISVIRTUAL_NOT]], label %[[MEMPTR_NONVIRTUAL:.*]], label %[[MEMPTR_VIRTUAL:.*]] +// CHECK: [[MEMPTR_VIRTUAL]]: +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 [[METHOD_COERCE1]] +// CHECK-NEXT: [[VTABLE:%.*]] = load ptr, ptr [[TMP1]], align 8, !tbaa [[TBAA2:![0-9]+]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[VTABLE]], i64 [[METHOD_COERCE0]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[TMP2]], i64 -1 +// CHECK-NEXT: [[MEMPTR_VIRTUALFN:%.*]] = load ptr, ptr [[TMP3]], align 8, !nosanitize [[META5:![0-9]+]] +// CHECK-NEXT: br label %[[MEMPTR_END:.*]] +// CHECK: [[MEMPTR_NONVIRTUAL]]: +// CHECK-NEXT: [[MEMPTR_NONVIRTUALFN:%.*]] = inttoptr i64 [[METHOD_COERCE0]] to ptr +// CHECK-NEXT: br label %[[MEMPTR_END]] +// CHECK: [[MEMPTR_END]]: +// CHECK-NEXT: [[TMP4:%.*]] = phi ptr [ [[MEMPTR_VIRTUALFN]], %[[MEMPTR_VIRTUAL]] ], [ [[MEMPTR_NONVIRTUALFN]], %[[MEMPTR_NONVIRTUAL]] ] +// CHECK-NEXT: ret ptr [[TMP4]] +// +pf_type convert_pmf(A* p, pmf_type method) { + return reinterpret_cast(p->*method); +} + +// CHECK-LABEL: define dso_local noundef nonnull ptr @_Z17convert_pmf_constP1A( +// CHECK-SAME: ptr noundef readnone captures(none) [[P:%.*]]) local_unnamed_addr #[[ATTR2:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret ptr @_ZN1A6methodEv +// +pf_type convert_pmf_const(A* p) { + return reinterpret_cast(p->*(&A::method)); +} + +// CHECK-LABEL: define dso_local noundef ptr @_Z18convert_vpmf_constP1A( +// CHECK-SAME: ptr noundef readonly captures(none) [[P:%.*]]) local_unnamed_addr #[[ATTR1]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[VTABLE:%.*]] = load ptr, ptr [[P]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[MEMPTR_VIRTUALFN:%.*]] = load ptr, ptr [[VTABLE]], align 8, !nosanitize [[META5]] +// CHECK-NEXT: ret ptr [[MEMPTR_VIRTUALFN]] +// +pf_type convert_vpmf_const(A* p) { + return reinterpret_cast(p->*(&A::virtual_method)); +} + +// CHECK-LABEL: define dso_local noundef range(i32 0, 2) i32 @_Z21call_b_virtual_methodP1B( +// CHECK-SAME: ptr noundef [[P:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[VTABLE_I:%.*]] = load ptr, ptr [[P]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[MEMPTR_VIRTUALFN_I:%.*]] = load ptr, ptr [[VTABLE_I]], align 8, !nosanitize [[META5]] +// CHECK-NEXT: [[CALL1:%.*]] = tail call noundef i32 [[MEMPTR_VIRTUALFN_I]](ptr noundef nonnull [[P]]) #[[ATTR3:[0-9]+]] +// CHECK-NEXT: [[VTABLE:%.*]] = load ptr, ptr [[P]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[VTABLE]], align 8 +// CHECK-NEXT: [[CALL2:%.*]] = tail call noundef i32 [[TMP0]](ptr noundef nonnull align 8 dereferenceable(16) [[P]]) #[[ATTR3]] +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[CALL1]], [[CALL2]] +// CHECK-NEXT: [[CONV:%.*]] = zext i1 [[CMP]] to i32 +// CHECK-NEXT: ret i32 [[CONV]] +// +int call_b_virtual_method(B* p) { + return convert_pmf(p, &A::virtual_method)(p) == p->virtual_method(); +} + +//. +// CHECK: attributes #[[ATTR0]] = { mustprogress nounwind "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK: attributes #[[ATTR1]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(read, inaccessiblemem: none) "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK: attributes #[[ATTR2]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK: attributes #[[ATTR3]] = { nounwind } +//. +// CHECK: [[META0:![0-9]+]] = !{i32 1, !"wchar_size", i32 4} +// CHECK: [[META1:![0-9]+]] = !{!"{{.*}}clang version {{.*}}"} +// CHECK: [[TBAA2]] = !{[[META3:![0-9]+]], [[META3]], i64 0} +// CHECK: [[META3]] = !{!"vtable pointer", [[META4:![0-9]+]], i64 0} +// CHECK: [[META4]] = !{!"Simple C++ TBAA"} +// CHECK: [[META5]] = !{} +//. diff --git a/clang/test/SemaCXX/pmf-conversions.cpp b/clang/test/SemaCXX/pmf-conversions.cpp new file mode 100644 index 0000000000000..bee54327e36b2 --- /dev/null +++ b/clang/test/SemaCXX/pmf-conversions.cpp @@ -0,0 +1,54 @@ +// RUN: %clang_cc1 -triple %itanium_abi_triple -fsyntax-only %s -verify + +struct S { + int a; + void method(); + void method_overload(); + void method_overload(int); +}; + +using pmf_type = void (S::*)(); +using pm_type = int S::*; +using pf_type = void (*)(S*); +using pf_type_mismatched = void (*)(S*, int); + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpmf-conversions" + +// constexpr pmf conversions are not supported yet. +constexpr pf_type method_constexpr = reinterpret_cast(&S::method); // expected-error {{constexpr variable 'method_constexpr' must be initialized by a constant expression}} expected-note {{reinterpret_cast is not allowed in a constant expression}} +pf_type method = reinterpret_cast(&S::method); + +void pmf_convert_no_object(pmf_type method, pm_type field) { + (void)reinterpret_cast(&S::method); + (void)reinterpret_cast(method); + (void)reinterpret_cast(((method))); + (void)(pf_type)(&S::method); + (void)(pf_type)(method); + (void)reinterpret_cast(&S::method); + (void)reinterpret_cast(method); + (void)reinterpret_cast(&S::a); // expected-error {{reinterpret_cast from 'int S::*' to 'pf_type' (aka 'void (*)(S *)') is not allowed}} + (void)reinterpret_cast(field); // expected-error {{reinterpret_cast from 'pm_type' (aka 'int S::*') to 'pf_type' (aka 'void (*)(S *)') is not allowed}} +} + +void pmf_convert_with_base(S* p, S& r, pmf_type method, pm_type field) { + (void)reinterpret_cast(p->*(&S::method)); + (void)reinterpret_cast(((p)->*((&S::method)))); + (void)reinterpret_cast(p->*method); + (void)reinterpret_cast(((p)->*(method))); + (void)reinterpret_cast(p->*(static_cast(&S::method_overload))); + (void)(pf_type)(p->*(&S::method)); + (void)(pf_type)(p->*method); + (void)reinterpret_cast(p->*method); + (void)reinterpret_cast(r.*method); + (void)reinterpret_cast(r.*method); + (void)reinterpret_cast(p->*(&S::a)); + (void)reinterpret_cast(p->*field); +} + +#pragma clang diagnostic pop + +void pmf_convert_warning(S *p, pmf_type method) { + (void)reinterpret_cast(method); // expected-warning {{converting the bound member function 'pmf_type' (aka 'void (S::*)()') to a function pointer 'pf_type' (aka 'void (*)(S *)') is a GNU extension}} + (void)reinterpret_cast(p->*method); // expected-warning {{converting the bound member function '' to a function pointer 'pf_type' (aka 'void (*)(S *)') is a GNU extension}} +}