https://github.com/dtcxzyw updated https://github.com/llvm/llvm-project/pull/135649
>From 6f0a3ba5852134d8bd04679438866e6f373f494a Mon Sep 17 00:00:00 2001 From: Yingwei Zheng <dtcxzyw2...@gmail.com> Date: Tue, 15 Apr 2025 12:12:19 +0800 Subject: [PATCH 1/6] [Clang] Add support for GCC bound member functions extension --- clang/include/clang/AST/OperationKinds.def | 4 ++ clang/include/clang/Basic/DiagnosticGroups.td | 32 +++++----- .../clang/Basic/DiagnosticSemaKinds.td | 4 ++ clang/include/clang/CIR/Dialect/IR/CIROps.td | 1 + clang/lib/AST/Expr.cpp | 5 ++ clang/lib/AST/ExprConstant.cpp | 2 + clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 1 + clang/lib/CodeGen/CGExpr.cpp | 1 + clang/lib/CodeGen/CGExprAgg.cpp | 2 + clang/lib/CodeGen/CGExprComplex.cpp | 1 + clang/lib/CodeGen/CGExprConstant.cpp | 1 + clang/lib/CodeGen/CGExprScalar.cpp | 1 + clang/lib/CodeGen/ItaniumCXXABI.cpp | 24 ++++++- clang/lib/Edit/RewriteObjCFoundationAPI.cpp | 1 + clang/lib/Sema/SemaCast.cpp | 63 +++++++++++++++++++ .../StaticAnalyzer/Core/BasicValueFactory.cpp | 3 +- clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp | 3 +- 17 files changed, 129 insertions(+), 20 deletions(-) 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 180ca39bc07e9..7b5fb514e94fb 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -5120,6 +5120,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 %0 to a function pointer " + "%1 is a GNU extension">, + InGroup<GNUPMFCast>; 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 4deed08d693ac..bae06543207b9 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -1864,6 +1864,11 @@ bool CastExpr::CastConsistency() const { assert(getSubExpr()->getType()->isMemberPointerType()); goto CheckNoBasePath; + case CK_BoundMemberFunctionToFunctionPointer: + assert(getType()->isFunctionPointerType()); + assert(getSubExpr()->getType()->isMemberPointerType()); + 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 f0732a8ea60af..a6f4541cc7e80 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 5f028f6d8c6ac..eeeb5c8f7a21f 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -5387,6 +5387,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 b016c6e36d1a8..bb665e6634e5a 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 e9a7ba509350c..9cf2dcf376133 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -2582,6 +2582,7 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) { } case CK_ReinterpretMemberPointer: + case CK_BoundMemberFunctionToFunctionPointer: case CK_BaseToDerivedMemberPointer: case CK_DerivedToBaseMemberPointer: { Value *Src = Visit(E); diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp index 35485dc6d867f..63cb2d8128454 100644 --- a/clang/lib/CodeGen/ItaniumCXXABI.cpp +++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -930,7 +930,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(); @@ -977,6 +978,10 @@ 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) { + llvm::errs() << *src << '\n'; + llvm_unreachable("TODO"); + } llvm::Constant *adj = getMemberPointerAdjustment(E); if (!adj) return src; @@ -1051,7 +1056,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(); @@ -1061,6 +1067,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 2824dfce1572c..6d048e8888242 100644 --- a/clang/lib/Sema/SemaCast.cpp +++ b/clang/lib/Sema/SemaCast.cpp @@ -250,6 +250,10 @@ static TryCastResult TryStaticMemberPointerUpcast(Sema &Self, ExprResult &SrcExp unsigned &msg, CastKind &Kind, CXXCastPath &BasePath); +static TryCastResult TryStaticMemberFunctionPointerToFunctionPointerCast( + 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, @@ -1430,6 +1434,13 @@ static TryCastResult TryStaticCast(Sema &Self, ExprResult &SrcExpr, } } + // GCC extension: convert a PMF constant into a function pointer. + tcr = TryStaticMemberFunctionPointerToFunctionPointerCast( + Self, SrcExpr, SrcType, DestType, CStyle, OpRange, msg, Kind, BasePath); + + if (tcr != TC_NotApplicable) + return tcr; + // Reverse pointer upcast. C++ 4.10p3 specifies pointer upcast. // C++ 5.2.9p8 additionally disallows a cast path through virtual inheritance. tcr = TryStaticPointerDowncast(Self, SrcType, DestType, CStyle, OpRange, msg, @@ -1834,6 +1845,58 @@ TryStaticMemberPointerUpcast(Sema &Self, ExprResult &SrcExpr, QualType SrcType, return TC_Success; } +/// TryStaticMemberFunctionPointerToFunctionPointerCast - Tests whether a +/// conversion from PMF constant to function pointer is valid. +TryCastResult TryStaticMemberFunctionPointerToFunctionPointerCast( + Sema &Self, ExprResult &SrcExpr, QualType SrcType, QualType DestType, + bool CStyle, SourceRange OpRange, unsigned &msg, CastKind &Kind, + CXXCastPath &BasePath) { + const TargetCXXABI &CXXABI = Self.Context.getTargetInfo().getCXXABI(); + if (!CXXABI.isItaniumFamily()) + return TC_NotApplicable; + + const PointerType *DestPtr = DestType->getAs<PointerType>(); + if (!DestPtr) + return TC_NotApplicable; + + const FunctionProtoType *DestFnType = + DestPtr->getPointeeType()->getAs<FunctionProtoType>(); + if (!DestFnType || DestFnType->getNumParams() == 0) + return TC_NotApplicable; + + auto *ClsPtr = DestFnType->getParamType(0)->getAs<PointerType>(); + if (!ClsPtr) + return TC_NotApplicable; + + auto *ClsRec = ClsPtr->getPointeeType()->getAs<RecordType>(); + if (!ClsRec) + return TC_NotApplicable; + + auto *ClsTy = ClsRec->getAsCXXRecordDecl(); + if (!ClsTy) + return TC_NotApplicable; + + auto EPI = DestFnType->getExtProtoInfo(); + EPI.TypeQuals = ClsPtr->getPointeeType().getQualifiers(); + auto FuncTy = + Self.Context.getFunctionType(DestFnType->getCallResultType(Self.Context), + DestFnType->param_types().drop_front(), EPI); + auto DestPMFTy = Self.Context.getMemberPointerType(FuncTy, nullptr, ClsTy); + + ExprResult Result = SrcExpr; + + if (SrcType.getCanonicalType() != DestPMFTy) { + TryCastResult Res = TryStaticMemberPointerUpcast( + Self, Result, SrcType, DestType, CStyle, OpRange, msg, Kind, BasePath); + if (Res != TC_Success) + return TC_NotApplicable; + } + + Kind = CK_BoundMemberFunctionToFunctionPointer; + msg = diag::ext_bound_member_function_conversion; + return TC_Extension; +} + /// TryStaticImplicitCast - Tests whether a conversion according to C++ 5.2.9p2 /// is valid: /// diff --git a/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp b/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp index 02f34bc30f554..0ef1a14450cc2 100644 --- a/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp +++ b/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp @@ -190,7 +190,8 @@ const PointerToMemberData *BasicValueFactory::accumCXXBase( const nonloc::PointerToMember &PTM, const CastKind &kind) { assert((kind == CK_DerivedToBaseMemberPointer || kind == CK_BaseToDerivedMemberPointer || - kind == CK_ReinterpretMemberPointer) && + kind == CK_ReinterpretMemberPointer || + kind == CK_BoundMemberFunctionToFunctionPointer) && "accumCXXBase called with wrong CastKind"); nonloc::PointerToMember::PTMDataType PTMDT = PTM.getPTMData(); const NamedDecl *ND = nullptr; diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp index 3d0a69a515ab8..e470f7df3cef0 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -505,7 +505,8 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, } case CK_DerivedToBaseMemberPointer: case CK_BaseToDerivedMemberPointer: - case CK_ReinterpretMemberPointer: { + case CK_ReinterpretMemberPointer: + case CK_BoundMemberFunctionToFunctionPointer: { SVal V = state->getSVal(Ex, LCtx); if (auto PTMSV = V.getAs<nonloc::PointerToMember>()) { SVal CastedPTMSV = >From 01eca7bde7521d368416cc4d57eec010b3a89e5d Mon Sep 17 00:00:00 2001 From: Yingwei Zheng <dtcxzyw2...@gmail.com> Date: Tue, 15 Apr 2025 13:16:21 +0800 Subject: [PATCH 2/6] Move to `TryReinterpretCast` --- .../clang/Basic/DiagnosticSemaKinds.td | 4 +- clang/lib/CodeGen/ItaniumCXXABI.cpp | 6 +- clang/lib/Sema/SemaCast.cpp | 89 ++++--------------- 3 files changed, 22 insertions(+), 77 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 7b5fb514e94fb..57ff6435daee8 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -5121,8 +5121,8 @@ 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 %0 to a function pointer " - "%1 is a GNU extension">, + : ExtWarn<"converting the bound member function %1 to a function pointer " + "%2 is a GNU extension">, InGroup<GNUPMFCast>; def note_possible_target_of_call : Note<"possible target for call">; def err_no_viable_destructor : Error< diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp index 63cb2d8128454..e846ed52f8d90 100644 --- a/clang/lib/CodeGen/ItaniumCXXABI.cpp +++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -978,10 +978,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) { - llvm::errs() << *src << '\n'; - llvm_unreachable("TODO"); - } + if (E->getCastKind() == CK_BoundMemberFunctionToFunctionPointer) + return Builder.CreateExtractValue(src, 0, "src.ptr"); llvm::Constant *adj = getMemberPointerAdjustment(E); if (!adj) return src; diff --git a/clang/lib/Sema/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp index 6d048e8888242..964988e6344f1 100644 --- a/clang/lib/Sema/SemaCast.cpp +++ b/clang/lib/Sema/SemaCast.cpp @@ -243,17 +243,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 TryStaticMemberFunctionPointerToFunctionPointerCast( - 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, @@ -1434,13 +1428,6 @@ static TryCastResult TryStaticCast(Sema &Self, ExprResult &SrcExpr, } } - // GCC extension: convert a PMF constant into a function pointer. - tcr = TryStaticMemberFunctionPointerToFunctionPointerCast( - Self, SrcExpr, SrcType, DestType, CStyle, OpRange, msg, Kind, BasePath); - - if (tcr != TC_NotApplicable) - return tcr; - // Reverse pointer upcast. C++ 4.10p3 specifies pointer upcast. // C++ 5.2.9p8 additionally disallows a cast path through virtual inheritance. tcr = TryStaticPointerDowncast(Self, SrcType, DestType, CStyle, OpRange, msg, @@ -1845,58 +1832,6 @@ TryStaticMemberPointerUpcast(Sema &Self, ExprResult &SrcExpr, QualType SrcType, return TC_Success; } -/// TryStaticMemberFunctionPointerToFunctionPointerCast - Tests whether a -/// conversion from PMF constant to function pointer is valid. -TryCastResult TryStaticMemberFunctionPointerToFunctionPointerCast( - Sema &Self, ExprResult &SrcExpr, QualType SrcType, QualType DestType, - bool CStyle, SourceRange OpRange, unsigned &msg, CastKind &Kind, - CXXCastPath &BasePath) { - const TargetCXXABI &CXXABI = Self.Context.getTargetInfo().getCXXABI(); - if (!CXXABI.isItaniumFamily()) - return TC_NotApplicable; - - const PointerType *DestPtr = DestType->getAs<PointerType>(); - if (!DestPtr) - return TC_NotApplicable; - - const FunctionProtoType *DestFnType = - DestPtr->getPointeeType()->getAs<FunctionProtoType>(); - if (!DestFnType || DestFnType->getNumParams() == 0) - return TC_NotApplicable; - - auto *ClsPtr = DestFnType->getParamType(0)->getAs<PointerType>(); - if (!ClsPtr) - return TC_NotApplicable; - - auto *ClsRec = ClsPtr->getPointeeType()->getAs<RecordType>(); - if (!ClsRec) - return TC_NotApplicable; - - auto *ClsTy = ClsRec->getAsCXXRecordDecl(); - if (!ClsTy) - return TC_NotApplicable; - - auto EPI = DestFnType->getExtProtoInfo(); - EPI.TypeQuals = ClsPtr->getPointeeType().getQualifiers(); - auto FuncTy = - Self.Context.getFunctionType(DestFnType->getCallResultType(Self.Context), - DestFnType->param_types().drop_front(), EPI); - auto DestPMFTy = Self.Context.getMemberPointerType(FuncTy, nullptr, ClsTy); - - ExprResult Result = SrcExpr; - - if (SrcType.getCanonicalType() != DestPMFTy) { - TryCastResult Res = TryStaticMemberPointerUpcast( - Self, Result, SrcType, DestType, CStyle, OpRange, msg, Kind, BasePath); - if (Res != TC_Success) - return TC_NotApplicable; - } - - Kind = CK_BoundMemberFunctionToFunctionPointer; - msg = diag::ext_bound_member_function_conversion; - return TC_Extension; -} - /// TryStaticImplicitCast - Tests whether a conversion according to C++ 5.2.9p2 /// is valid: /// @@ -2352,7 +2287,19 @@ static TryCastResult TryReinterpretCast(Sema &Self, ExprResult &SrcExpr, const MemberPointerType *DestMemPtr = DestType->getAs<MemberPointerType>(), *SrcMemPtr = SrcType->getAs<MemberPointerType>(); - if (DestMemPtr && SrcMemPtr) { + if (SrcMemPtr) { + // GNU extension: check if we can convert a pmf to a function pointer + if (!DestMemPtr) { + if (DestType->isFunctionPointerType() && + SrcMemPtr->isMemberFunctionPointer() && + Self.Context.getTargetInfo().getCXXABI().isItaniumFamily()) { + Kind = CK_BoundMemberFunctionToFunctionPointer; + msg = diag::ext_bound_member_function_conversion; + return TC_Extension; + } + return TC_NotApplicable; + } + // C++ 5.2.10p9: An rvalue of type "pointer to member of X of type T1" // can be explicitly converted to an rvalue of type "pointer to member // of Y of type T2" if T1 and T2 are both function types or both object >From a8d60983b49e36eb66c395fead0fb65a922233c9 Mon Sep 17 00:00:00 2001 From: Yingwei Zheng <dtcxzyw2...@gmail.com> Date: Tue, 15 Apr 2025 13:23:43 +0800 Subject: [PATCH 3/6] Remove incorrect static analyzer support --- clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp | 3 +-- clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp b/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp index 0ef1a14450cc2..02f34bc30f554 100644 --- a/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp +++ b/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp @@ -190,8 +190,7 @@ const PointerToMemberData *BasicValueFactory::accumCXXBase( const nonloc::PointerToMember &PTM, const CastKind &kind) { assert((kind == CK_DerivedToBaseMemberPointer || kind == CK_BaseToDerivedMemberPointer || - kind == CK_ReinterpretMemberPointer || - kind == CK_BoundMemberFunctionToFunctionPointer) && + kind == CK_ReinterpretMemberPointer) && "accumCXXBase called with wrong CastKind"); nonloc::PointerToMember::PTMDataType PTMDT = PTM.getPTMData(); const NamedDecl *ND = nullptr; diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp index e470f7df3cef0..d28752febce43 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -505,8 +505,7 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, } case CK_DerivedToBaseMemberPointer: case CK_BaseToDerivedMemberPointer: - case CK_ReinterpretMemberPointer: - case CK_BoundMemberFunctionToFunctionPointer: { + case CK_ReinterpretMemberPointer: { SVal V = state->getSVal(Ex, LCtx); if (auto PTMSV = V.getAs<nonloc::PointerToMember>()) { SVal CastedPTMSV = @@ -525,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); >From bfdeefdbe2959e1cedbd0f2d9142b8c9c0b20de1 Mon Sep 17 00:00:00 2001 From: Yingwei Zheng <dtcxzyw2...@gmail.com> Date: Tue, 15 Apr 2025 16:29:11 +0800 Subject: [PATCH 4/6] [Clang] Add codegen support --- clang/lib/AST/Expr.cpp | 4 +++- clang/lib/CodeGen/CGExprScalar.cpp | 33 +++++++++++++++++++++++++++++- clang/lib/Sema/SemaCast.cpp | 32 ++++++++++++++--------------- 3 files changed, 51 insertions(+), 18 deletions(-) diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index bae06543207b9..967fb42668902 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -1866,7 +1866,9 @@ bool CastExpr::CastConsistency() const { case CK_BoundMemberFunctionToFunctionPointer: assert(getType()->isFunctionPointerType()); - assert(getSubExpr()->getType()->isMemberPointerType()); + assert(getSubExpr()->getType()->isMemberPointerType() || + getSubExpr()->getType()->isSpecificPlaceholderType( + BuiltinType::BoundMember)); goto CheckNoBasePath; case CK_BitCast: diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index 9cf2dcf376133..b2cca91f1b733 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -2581,8 +2581,39 @@ 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<BinaryOperator>(E->IgnoreParens()); + const Expr *BaseExpr = BO->getLHS(); + const Expr *MemFnExpr = BO->getRHS(); + + const auto *MPT = MemFnExpr->getType()->castAs<MemberPointerType>(); + const auto *FPT = MPT->getPointeeType()->castAs<FunctionProtoType>(); + 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_BoundMemberFunctionToFunctionPointer: case CK_BaseToDerivedMemberPointer: case CK_DerivedToBaseMemberPointer: { Value *Src = Visit(E); diff --git a/clang/lib/Sema/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp index 964988e6344f1..ec4d4e5f3f370 100644 --- a/clang/lib/Sema/SemaCast.cpp +++ b/clang/lib/Sema/SemaCast.cpp @@ -1196,9 +1196,11 @@ static unsigned int checkCastFunctionType(Sema &Self, const ExprResult &SrcExpr, /// like this: /// char *bytes = reinterpret_cast\<char*\>(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) && + !isPlaceholder(BuiltinType::BoundMember)) + SrcExpr = Self.DefaultFunctionArrayLvalueConversion(SrcExpr.get()); + } else checkNonOverloadPlaceholders(); if (SrcExpr.isInvalid()) // if conversion failed, don't report another error return; @@ -2287,19 +2289,7 @@ static TryCastResult TryReinterpretCast(Sema &Self, ExprResult &SrcExpr, const MemberPointerType *DestMemPtr = DestType->getAs<MemberPointerType>(), *SrcMemPtr = SrcType->getAs<MemberPointerType>(); - if (SrcMemPtr) { - // GNU extension: check if we can convert a pmf to a function pointer - if (!DestMemPtr) { - if (DestType->isFunctionPointerType() && - SrcMemPtr->isMemberFunctionPointer() && - Self.Context.getTargetInfo().getCXXABI().isItaniumFamily()) { - Kind = CK_BoundMemberFunctionToFunctionPointer; - msg = diag::ext_bound_member_function_conversion; - return TC_Extension; - } - return TC_NotApplicable; - } - + if (SrcMemPtr && DestMemPtr) { // C++ 5.2.10p9: An rvalue of type "pointer to member of X of type T1" // can be explicitly converted to an rvalue of type "pointer to member // of Y of type T2" if T1 and T2 are both function types or both object @@ -2337,6 +2327,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 >From db282232bdb94fbc1237377cf978f9178950a941 Mon Sep 17 00:00:00 2001 From: Yingwei Zheng <dtcxzyw2...@gmail.com> Date: Tue, 15 Apr 2025 17:56:29 +0800 Subject: [PATCH 5/6] Fix test --- clang/lib/Sema/SemaCast.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clang/lib/Sema/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp index ec4d4e5f3f370..1934b9830852b 100644 --- a/clang/lib/Sema/SemaCast.cpp +++ b/clang/lib/Sema/SemaCast.cpp @@ -1198,7 +1198,8 @@ static unsigned int checkCastFunctionType(Sema &Self, const ExprResult &SrcExpr, void CastOperation::CheckReinterpretCast() { if (ValueKind == VK_PRValue) { if (!isPlaceholder(BuiltinType::Overload) && - !isPlaceholder(BuiltinType::BoundMember)) + !(isPlaceholder(BuiltinType::BoundMember) && + DestType->isFunctionPointerType())) SrcExpr = Self.DefaultFunctionArrayLvalueConversion(SrcExpr.get()); } else checkNonOverloadPlaceholders(); >From 9f04b0751d04c18292f9d1c25e239b3ead87c48d Mon Sep 17 00:00:00 2001 From: Yingwei Zheng <dtcxzyw2...@gmail.com> Date: Tue, 15 Apr 2025 21:48:22 +0800 Subject: [PATCH 6/6] Add tests. NFC. --- clang/test/CodeGenCXX/pmf-conversions.cpp | 105 ++++++++++++++++++++++ clang/test/SemaCXX/pmf-conversions.cpp | 50 +++++++++++ 2 files changed, 155 insertions(+) create mode 100644 clang/test/CodeGenCXX/pmf-conversions.cpp create mode 100644 clang/test/SemaCXX/pmf-conversions.cpp diff --git a/clang/test/CodeGenCXX/pmf-conversions.cpp b/clang/test/CodeGenCXX/pmf-conversions.cpp new file mode 100644 index 0000000000000..cbe2cd4d09bd6 --- /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 --check-globals + +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<pf_type>(&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<pf_type>(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<pf_type>(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<pf_type>(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..e683d5d314072 --- /dev/null +++ b/clang/test/SemaCXX/pmf-conversions.cpp @@ -0,0 +1,50 @@ +// 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" + +void pmf_convert_no_object(pmf_type method, pm_type field) { + (void)reinterpret_cast<pf_type>(&S::method); + (void)reinterpret_cast<pf_type>(method); + (void)reinterpret_cast<pf_type>(((method))); + (void)(pf_type)(&S::method); + (void)(pf_type)(method); + (void)reinterpret_cast<pf_type_mismatched>(&S::method); + (void)reinterpret_cast<pf_type_mismatched>(method); + (void)reinterpret_cast<pf_type>(&S::a); // expected-error {{reinterpret_cast from 'int S::*' to 'pf_type' (aka 'void (*)(S *)') is not allowed}} + (void)reinterpret_cast<pf_type>(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<pf_type>(p->*(&S::method)); + (void)reinterpret_cast<pf_type>(((p)->*((&S::method)))); + (void)reinterpret_cast<pf_type>(p->*method); + (void)reinterpret_cast<pf_type>(((p)->*(method))); + (void)reinterpret_cast<pf_type>(p->*(static_cast<pmf_type>(&S::method_overload))); + (void)(pf_type)(p->*(&S::method)); // expected-error {{reference to non-static member function must be called; did you mean to call it with no arguments?}} expected-error {{cannot cast from type 'void' to pointer type 'pf_type' (aka 'void (*)(S *)')}} + (void)(pf_type)(p->*method); // expected-error {{reference to non-static member function must be called; did you mean to call it with no arguments?}} expected-error {{cannot cast from type 'void' to pointer type 'pf_type' (aka 'void (*)(S *)')}} + (void)reinterpret_cast<pf_type_mismatched>(p->*method); + (void)reinterpret_cast<pf_type>(r.*method); + (void)reinterpret_cast<pf_type_mismatched>(r.*method); + (void)reinterpret_cast<pf_type>(p->*(&S::a)); + (void)reinterpret_cast<pf_type>(p->*field); +} + +#pragma clang diagnostic pop + +void pmf_convert_warning(S *p, pmf_type method) { + (void)reinterpret_cast<pf_type>(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<pf_type>(p->*method); // expected-warning {{converting the bound member function '<bound member function type>' to a function pointer 'pf_type' (aka 'void (*)(S *)') is a GNU extension}} +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits