[clang] [Clang] Add support for GCC bound member functions extension (PR #135649)
Yingwei Zheng via cfe-commits
cfe-commits at lists.llvm.org
Mon Apr 14 21:12:49 PDT 2025
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 <dtcxzyw2333 at gmail.com>
Date: Tue, 15 Apr 2025 12:12:19 +0800
Subject: [PATCH] [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 =
More information about the cfe-commits
mailing list