llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang-static-analyzer-1 Author: Yingwei Zheng (dtcxzyw) <details> <summary>Changes</summary> This patch adds support for GCC bound member functions extension: https://gcc.gnu.org/onlinedocs/gcc/Bound-member-functions.html Closes https://github.com/llvm/llvm-project/issues/82727 --- Patch is 29.68 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/135649.diff 19 Files Affected: - (modified) clang/docs/ReleaseNotes.rst (+5) - (modified) clang/include/clang/AST/OperationKinds.def (+4) - (modified) clang/include/clang/Basic/DiagnosticGroups.td (+16-16) - (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+4) - (modified) clang/include/clang/CIR/Dialect/IR/CIROps.td (+1) - (modified) clang/lib/AST/Expr.cpp (+7) - (modified) clang/lib/AST/ExprConstant.cpp (+2) - (modified) clang/lib/CIR/CodeGen/CIRGenExpr.cpp (+1) - (modified) clang/lib/CodeGen/CGExpr.cpp (+1) - (modified) clang/lib/CodeGen/CGExprAgg.cpp (+2) - (modified) clang/lib/CodeGen/CGExprComplex.cpp (+1) - (modified) clang/lib/CodeGen/CGExprConstant.cpp (+1) - (modified) clang/lib/CodeGen/CGExprScalar.cpp (+32) - (modified) clang/lib/CodeGen/ItaniumCXXABI.cpp (+20-2) - (modified) clang/lib/Edit/RewriteObjCFoundationAPI.cpp (+1) - (modified) clang/lib/Sema/SemaCast.cpp (+40-20) - (modified) clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp (+2-1) - (added) clang/test/CodeGenCXX/pmf-conversions.cpp (+105) - (added) clang/test/SemaCXX/pmf-conversions.cpp (+54) ``````````diff 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 <https://gcc.gnu.org/onlinedocs/gcc/Bound-member-functions.html>`_ 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<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 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<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_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\<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) && !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 immediat... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/135649 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits