Skip to content

Commit 43c487c

Browse files
committed
[Clang] Add support for GCC bound member functions extension
1 parent e710a5a commit 43c487c

19 files changed

+299
-39
lines changed

clang/docs/ReleaseNotes.rst

+5
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,11 @@ C++ Language Changes
9393
asm((std::string_view("nop")) ::: (std::string_view("memory")));
9494
}
9595

96+
- Implemented `GCC bound member functions extension <https://gcc.gnu.org/onlinedocs/gcc/Bound-member-functions.html>`_.
97+
This extension allows extracting the function pointer from a bound pointer to member function.
98+
It is useful to save vtable lookups when the same member function is executed multiple times inside a loop.
99+
When using this extension, a warning is emitted unless ``-Wno-pmf-conversions`` is passed.
100+
96101
C++2c Feature Support
97102
^^^^^^^^^^^^^^^^^^^^^
98103

clang/include/clang/AST/OperationKinds.def

+4
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,10 @@ CAST_OPERATION(MemberPointerToBoolean)
152152
/// many ABIs do not guarantee this on all possible intermediate types).
153153
CAST_OPERATION(ReinterpretMemberPointer)
154154

155+
/// CK_BoundPointerToMemberFunctionToFunctionPointer - Convert a bound
156+
/// member function pointer to a function pointer. This is a GNU extension.
157+
CAST_OPERATION(BoundMemberFunctionToFunctionPointer)
158+
155159
/// CK_UserDefinedConversion - Conversion using a user defined type
156160
/// conversion function.
157161
/// struct A { operator int(); }; int i = int(A());

clang/include/clang/Basic/DiagnosticGroups.td

+16-16
Original file line numberDiff line numberDiff line change
@@ -795,6 +795,7 @@ def DuplicateDeclSpecifier : DiagGroup<"duplicate-decl-specifier">;
795795
def CompareDistinctPointerType : DiagGroup<"compare-distinct-pointer-types">;
796796
def GNUUnionCast : DiagGroup<"gnu-union-cast">;
797797
def GNUVariableSizedTypeNotAtEnd : DiagGroup<"gnu-variable-sized-type-not-at-end">;
798+
def GNUPMFCast : DiagGroup<"pmf-conversions">;
798799
def Varargs : DiagGroup<"varargs">;
799800
def XorUsedAsPow : DiagGroup<"xor-used-as-pow">;
800801

@@ -1294,22 +1295,21 @@ def C2y : DiagGroup<"c2y-extensions">;
12941295
def GNUBinaryLiteral : DiagGroup<"gnu-binary-literal">;
12951296

12961297
// A warning group for warnings about GCC extensions.
1297-
def GNU : DiagGroup<"gnu", [GNUAlignofExpression, GNUAnonymousStruct,
1298-
GNUAutoType, GNUBinaryLiteral, GNUCaseRange,
1299-
GNUComplexInteger, GNUCompoundLiteralInitializer,
1300-
GNUConditionalOmittedOperand, GNUDesignator,
1301-
GNUEmptyStruct,
1302-
VLAExtension, GNUFlexibleArrayInitializer,
1303-
GNUFlexibleArrayUnionMember, GNUFoldingConstant,
1304-
GNUImaginaryConstant, GNUIncludeNext,
1305-
GNULabelsAsValue, GNULineMarker, GNUNullPointerArithmetic,
1306-
GNUOffsetofExtensions, GNUPointerArith,
1307-
RedeclaredClassMember, GNURedeclaredEnum,
1308-
GNUStatementExpression, GNUStaticFloatInit,
1309-
GNUStringLiteralOperatorTemplate, GNUUnionCast,
1310-
GNUVariableSizedTypeNotAtEnd, ZeroLengthArray,
1311-
GNUZeroLineDirective,
1312-
GNUZeroVariadicMacroArguments]>;
1298+
def GNU
1299+
: DiagGroup<
1300+
"gnu", [GNUAlignofExpression, GNUAnonymousStruct, GNUAutoType,
1301+
GNUBinaryLiteral, GNUCaseRange, GNUComplexInteger,
1302+
GNUCompoundLiteralInitializer, GNUConditionalOmittedOperand,
1303+
GNUDesignator, GNUEmptyStruct, VLAExtension,
1304+
GNUFlexibleArrayInitializer, GNUFlexibleArrayUnionMember,
1305+
GNUFoldingConstant, GNUImaginaryConstant, GNUIncludeNext,
1306+
GNULabelsAsValue, GNULineMarker, GNUNullPointerArithmetic,
1307+
GNUOffsetofExtensions, GNUPointerArith, RedeclaredClassMember,
1308+
GNURedeclaredEnum, GNUStatementExpression, GNUStaticFloatInit,
1309+
GNUStringLiteralOperatorTemplate, GNUUnionCast,
1310+
GNUVariableSizedTypeNotAtEnd, ZeroLengthArray,
1311+
GNUZeroLineDirective, GNUZeroVariadicMacroArguments,
1312+
GNUPMFCast]>;
13131313
// A warning group for warnings about code that clang accepts but gcc doesn't.
13141314
def GccCompat : DiagGroup<"gcc-compat">;
13151315

clang/include/clang/Basic/DiagnosticSemaKinds.td

+4
Original file line numberDiff line numberDiff line change
@@ -5120,6 +5120,10 @@ def err_ovl_unresolvable : Error<
51205120
def err_bound_member_function : Error<
51215121
"reference to non-static member function must be called"
51225122
"%select{|; did you mean to call it with no arguments?}0">;
5123+
def ext_bound_member_function_conversion
5124+
: ExtWarn<"converting the bound member function %1 to a function pointer "
5125+
"%2 is a GNU extension">,
5126+
InGroup<GNUPMFCast>;
51235127
def note_possible_target_of_call : Note<"possible target for call">;
51245128
def err_no_viable_destructor : Error<
51255129
"no viable destructor found for class %0">;

clang/include/clang/CIR/Dialect/IR/CIROps.td

+1
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ def CK_ArrayToPointerDecay : I32EnumAttrCase<"array_to_ptrdecay", 11>;
109109
// CK_DerivedToBaseMemberPointer
110110
def CK_MemberPointerToBoolean : I32EnumAttrCase<"member_ptr_to_bool", 17>;
111111
// CK_ReinterpretMemberPointer
112+
// CK_BoundMemberFunctionToFunctionPointer
112113
// CK_UserDefinedConversion
113114
// CK_ConstructorConversion
114115
def CK_IntegralToPointer : I32EnumAttrCase<"int_to_ptr", 21>;

clang/lib/AST/Expr.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -1864,6 +1864,13 @@ bool CastExpr::CastConsistency() const {
18641864
assert(getSubExpr()->getType()->isMemberPointerType());
18651865
goto CheckNoBasePath;
18661866

1867+
case CK_BoundMemberFunctionToFunctionPointer:
1868+
assert(getType()->isFunctionPointerType());
1869+
assert(getSubExpr()->getType()->isMemberPointerType() ||
1870+
getSubExpr()->getType()->isSpecificPlaceholderType(
1871+
BuiltinType::BoundMember));
1872+
goto CheckNoBasePath;
1873+
18671874
case CK_BitCast:
18681875
// Arbitrary casts to C pointer types count as bitcasts.
18691876
// Otherwise, we should only have block and ObjC pointer casts

clang/lib/AST/ExprConstant.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -15103,6 +15103,7 @@ bool IntExprEvaluator::VisitCastExpr(const CastExpr *E) {
1510315103
case CK_BaseToDerivedMemberPointer:
1510415104
case CK_DerivedToBaseMemberPointer:
1510515105
case CK_ReinterpretMemberPointer:
15106+
case CK_BoundMemberFunctionToFunctionPointer:
1510615107
case CK_ConstructorConversion:
1510715108
case CK_IntegralToPointer:
1510815109
case CK_ToVoid:
@@ -15960,6 +15961,7 @@ bool ComplexExprEvaluator::VisitCastExpr(const CastExpr *E) {
1596015961
case CK_DerivedToBaseMemberPointer:
1596115962
case CK_MemberPointerToBoolean:
1596215963
case CK_ReinterpretMemberPointer:
15964+
case CK_BoundMemberFunctionToFunctionPointer:
1596315965
case CK_ConstructorConversion:
1596415966
case CK_IntegralToPointer:
1596515967
case CK_PointerToIntegral:

clang/lib/CIR/CodeGen/CIRGenExpr.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ Address CIRGenFunction::emitPointerWithAlignment(const Expr *expr,
8383
case CK_NullToMemberPointer:
8484
case CK_NullToPointer:
8585
case CK_ReinterpretMemberPointer:
86+
case CK_BoundMemberFunctionToFunctionPointer:
8687
// Common pointer conversions, nothing to do here.
8788
// TODO: Is there any reason to treat base-to-derived conversions
8889
// specially?

clang/lib/CodeGen/CGExpr.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -5387,6 +5387,7 @@ LValue CodeGenFunction::EmitCastLValue(const CastExpr *E) {
53875387
case CK_BaseToDerivedMemberPointer:
53885388
case CK_MemberPointerToBoolean:
53895389
case CK_ReinterpretMemberPointer:
5390+
case CK_BoundMemberFunctionToFunctionPointer:
53905391
case CK_AnyPointerToBlockPointerCast:
53915392
case CK_ARCProduceObject:
53925393
case CK_ARCConsumeObject:

clang/lib/CodeGen/CGExprAgg.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -1043,6 +1043,7 @@ void AggExprEmitter::VisitCastExpr(CastExpr *E) {
10431043
case CK_DerivedToBaseMemberPointer:
10441044
case CK_MemberPointerToBoolean:
10451045
case CK_ReinterpretMemberPointer:
1046+
case CK_BoundMemberFunctionToFunctionPointer:
10461047
case CK_IntegralToPointer:
10471048
case CK_PointerToIntegral:
10481049
case CK_PointerToBoolean:
@@ -1600,6 +1601,7 @@ static bool castPreservesZero(const CastExpr *CE) {
16001601
case CK_MemberPointerToBoolean:
16011602
case CK_NullToMemberPointer:
16021603
case CK_ReinterpretMemberPointer:
1604+
case CK_BoundMemberFunctionToFunctionPointer:
16031605
// FIXME: ABI-dependent.
16041606
return false;
16051607

clang/lib/CodeGen/CGExprComplex.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,7 @@ ComplexPairTy ComplexExprEmitter::EmitCast(CastKind CK, Expr *Op,
575575
case CK_DerivedToBaseMemberPointer:
576576
case CK_MemberPointerToBoolean:
577577
case CK_ReinterpretMemberPointer:
578+
case CK_BoundMemberFunctionToFunctionPointer:
578579
case CK_ConstructorConversion:
579580
case CK_IntegralToPointer:
580581
case CK_PointerToIntegral:

clang/lib/CodeGen/CGExprConstant.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -1272,6 +1272,7 @@ class ConstExprEmitter
12721272
llvm_unreachable("builtin functions are handled elsewhere");
12731273

12741274
case CK_ReinterpretMemberPointer:
1275+
case CK_BoundMemberFunctionToFunctionPointer:
12751276
case CK_DerivedToBaseMemberPointer:
12761277
case CK_BaseToDerivedMemberPointer: {
12771278
auto C = Emitter.tryEmitPrivate(subExpr, subExpr->getType());

clang/lib/CodeGen/CGExprScalar.cpp

+32
Original file line numberDiff line numberDiff line change
@@ -2581,6 +2581,38 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
25812581
return CGF.CGM.getCXXABI().EmitNullMemberPointer(MPT);
25822582
}
25832583

2584+
case CK_BoundMemberFunctionToFunctionPointer: {
2585+
// Special handling bound member functions
2586+
if (E->isBoundMemberFunction(CGF.getContext())) {
2587+
auto *BO = cast<BinaryOperator>(E->IgnoreParens());
2588+
const Expr *BaseExpr = BO->getLHS();
2589+
const Expr *MemFnExpr = BO->getRHS();
2590+
2591+
const auto *MPT = MemFnExpr->getType()->castAs<MemberPointerType>();
2592+
const auto *FPT = MPT->getPointeeType()->castAs<FunctionProtoType>();
2593+
const auto *RD = MPT->getMostRecentCXXRecordDecl();
2594+
2595+
// Emit the 'this' pointer.
2596+
Address This = Address::invalid();
2597+
if (BO->getOpcode() == BO_PtrMemI)
2598+
This = CGF.EmitPointerWithAlignment(BaseExpr, nullptr, nullptr,
2599+
KnownNonNull);
2600+
else
2601+
This = CGF.EmitLValue(BaseExpr, KnownNonNull).getAddress();
2602+
2603+
// Get the member function pointer.
2604+
llvm::Value *MemFnPtr = CGF.EmitScalarExpr(MemFnExpr);
2605+
2606+
// Ask the ABI to load the callee. Note that This is modified.
2607+
llvm::Value *ThisPtrForCall = nullptr;
2608+
CGCallee Callee = CGF.CGM.getCXXABI().EmitLoadOfMemberFunctionPointer(
2609+
CGF, BO, This, ThisPtrForCall, MemFnPtr, MPT);
2610+
return Callee.getFunctionPointer();
2611+
}
2612+
2613+
// fallback to the case without the base object address
2614+
}
2615+
[[fallthrough]];
25842616
case CK_ReinterpretMemberPointer:
25852617
case CK_BaseToDerivedMemberPointer:
25862618
case CK_DerivedToBaseMemberPointer: {

clang/lib/CodeGen/ItaniumCXXABI.cpp

+20-2
Original file line numberDiff line numberDiff line change
@@ -930,7 +930,8 @@ ItaniumCXXABI::EmitMemberPointerConversion(CodeGenFunction &CGF,
930930

931931
assert(E->getCastKind() == CK_DerivedToBaseMemberPointer ||
932932
E->getCastKind() == CK_BaseToDerivedMemberPointer ||
933-
E->getCastKind() == CK_ReinterpretMemberPointer);
933+
E->getCastKind() == CK_ReinterpretMemberPointer ||
934+
E->getCastKind() == CK_BoundMemberFunctionToFunctionPointer);
934935

935936
CGBuilderTy &Builder = CGF.Builder;
936937
QualType DstType = E->getType();
@@ -977,6 +978,8 @@ ItaniumCXXABI::EmitMemberPointerConversion(CodeGenFunction &CGF,
977978

978979
// Under Itanium, reinterprets don't require any additional processing.
979980
if (E->getCastKind() == CK_ReinterpretMemberPointer) return src;
981+
if (E->getCastKind() == CK_BoundMemberFunctionToFunctionPointer)
982+
return Builder.CreateExtractValue(src, 0, "src.ptr");
980983

981984
llvm::Constant *adj = getMemberPointerAdjustment(E);
982985
if (!adj) return src;
@@ -1051,7 +1054,8 @@ ItaniumCXXABI::EmitMemberPointerConversion(const CastExpr *E,
10511054
llvm::Constant *src) {
10521055
assert(E->getCastKind() == CK_DerivedToBaseMemberPointer ||
10531056
E->getCastKind() == CK_BaseToDerivedMemberPointer ||
1054-
E->getCastKind() == CK_ReinterpretMemberPointer);
1057+
E->getCastKind() == CK_ReinterpretMemberPointer ||
1058+
E->getCastKind() == CK_BoundMemberFunctionToFunctionPointer);
10551059

10561060
QualType DstType = E->getType();
10571061

@@ -1061,6 +1065,20 @@ ItaniumCXXABI::EmitMemberPointerConversion(const CastExpr *E,
10611065

10621066
// Under Itanium, reinterprets don't require any additional processing.
10631067
if (E->getCastKind() == CK_ReinterpretMemberPointer) return src;
1068+
if (E->getCastKind() == CK_BoundMemberFunctionToFunctionPointer) {
1069+
llvm::Type *PtrTy = llvm::PointerType::getUnqual(CGM.getLLVMContext());
1070+
llvm::Constant *FuncPtr = llvm::ConstantExpr::getIntToPtr(
1071+
ConstantFoldExtractValueInstruction(src, 0), PtrTy);
1072+
1073+
const auto &NewAuthInfo = CGM.getFunctionPointerAuthInfo(DstType);
1074+
const auto &CurAuthInfo =
1075+
CGM.getMemberFunctionPointerAuthInfo(E->getSubExpr()->getType());
1076+
1077+
if (!NewAuthInfo && !CurAuthInfo)
1078+
return FuncPtr;
1079+
1080+
return pointerAuthResignConstant(FuncPtr, CurAuthInfo, NewAuthInfo, CGM);
1081+
}
10641082

10651083
// If the adjustment is trivial, we don't need to do anything.
10661084
llvm::Constant *adj = getMemberPointerAdjustment(E);

clang/lib/Edit/RewriteObjCFoundationAPI.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -1054,6 +1054,7 @@ static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
10541054
case CK_DerivedToBaseMemberPointer:
10551055
case CK_MemberPointerToBoolean:
10561056
case CK_ReinterpretMemberPointer:
1057+
case CK_BoundMemberFunctionToFunctionPointer:
10571058
case CK_ConstructorConversion:
10581059
case CK_IntegralToPointer:
10591060
case CK_PointerToIntegral:

clang/lib/Sema/SemaCast.cpp

+40-20
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,11 @@ namespace {
150150
bool isPlaceholder(BuiltinType::Kind K) const {
151151
return PlaceholderKind == K;
152152
}
153+
bool isBoundPMFConversion() const {
154+
return isPlaceholder(BuiltinType::BoundMember) &&
155+
DestType->isFunctionPointerType() &&
156+
SrcExpr.get()->isBoundMemberFunction(Self.Context);
157+
}
153158

154159
// Language specific cast restrictions for address spaces.
155160
void checkAddressSpaceCast(QualType SrcType, QualType DestType);
@@ -243,13 +248,11 @@ static TryCastResult TryStaticDowncast(Sema &Self, CanQualType SrcType,
243248
QualType OrigDestType, unsigned &msg,
244249
CastKind &Kind,
245250
CXXCastPath &BasePath);
246-
static TryCastResult TryStaticMemberPointerUpcast(Sema &Self, ExprResult &SrcExpr,
247-
QualType SrcType,
248-
QualType DestType,bool CStyle,
249-
SourceRange OpRange,
250-
unsigned &msg,
251-
CastKind &Kind,
252-
CXXCastPath &BasePath);
251+
static TryCastResult
252+
TryStaticMemberPointerUpcast(Sema &Self, ExprResult &SrcExpr, QualType SrcType,
253+
QualType DestType, bool CStyle,
254+
SourceRange OpRange, unsigned &msg, CastKind &Kind,
255+
CXXCastPath &BasePath);
253256

254257
static TryCastResult
255258
TryStaticImplicitCast(Sema &Self, ExprResult &SrcExpr, QualType DestType,
@@ -1198,9 +1201,10 @@ static unsigned int checkCastFunctionType(Sema &Self, const ExprResult &SrcExpr,
11981201
/// like this:
11991202
/// char *bytes = reinterpret_cast\<char*\>(int_ptr);
12001203
void CastOperation::CheckReinterpretCast() {
1201-
if (ValueKind == VK_PRValue && !isPlaceholder(BuiltinType::Overload))
1202-
SrcExpr = Self.DefaultFunctionArrayLvalueConversion(SrcExpr.get());
1203-
else
1204+
if (ValueKind == VK_PRValue) {
1205+
if (!isPlaceholder(BuiltinType::Overload) && !isBoundPMFConversion())
1206+
SrcExpr = Self.DefaultFunctionArrayLvalueConversion(SrcExpr.get());
1207+
} else
12041208
checkNonOverloadPlaceholders();
12051209
if (SrcExpr.isInvalid()) // if conversion failed, don't report another error
12061210
return;
@@ -2327,6 +2331,16 @@ static TryCastResult TryReinterpretCast(Sema &Self, ExprResult &SrcExpr,
23272331
return TC_Success;
23282332
}
23292333

2334+
// GNU extension: check if we can convert a pmf to a function pointer
2335+
if (DestType->isFunctionPointerType() &&
2336+
(SrcType->isMemberFunctionPointerType() ||
2337+
SrcExpr.get()->isBoundMemberFunction(Self.Context)) &&
2338+
Self.Context.getTargetInfo().getCXXABI().isItaniumFamily()) {
2339+
Kind = CK_BoundMemberFunctionToFunctionPointer;
2340+
msg = diag::ext_bound_member_function_conversion;
2341+
return TC_Extension;
2342+
}
2343+
23302344
// See below for the enumeral issue.
23312345
if (SrcType->isNullPtrType() && DestType->isIntegralType(Self.Context)) {
23322346
// C++0x 5.2.10p4: A pointer can be explicitly converted to any integral
@@ -2693,9 +2707,11 @@ void CastOperation::CheckCXXCStyleCast(bool FunctionalStyle,
26932707
return;
26942708
}
26952709

2696-
checkNonOverloadPlaceholders();
2697-
if (SrcExpr.isInvalid())
2698-
return;
2710+
if (!isBoundPMFConversion()) {
2711+
checkNonOverloadPlaceholders();
2712+
if (SrcExpr.isInvalid())
2713+
return;
2714+
}
26992715
}
27002716

27012717
// C++ 5.2.9p4: Any expression can be explicitly converted to type "cv void".
@@ -2733,7 +2749,7 @@ void CastOperation::CheckCXXCStyleCast(bool FunctionalStyle,
27332749
}
27342750

27352751
if (ValueKind == VK_PRValue && !DestType->isRecordType() &&
2736-
!isPlaceholder(BuiltinType::Overload)) {
2752+
!isPlaceholder(BuiltinType::Overload) && !isBoundPMFConversion()) {
27372753
SrcExpr = Self.DefaultFunctionArrayLvalueConversion(SrcExpr.get());
27382754
if (SrcExpr.isInvalid())
27392755
return;
@@ -2791,12 +2807,16 @@ void CastOperation::CheckCXXCStyleCast(bool FunctionalStyle,
27912807
return;
27922808

27932809
if (tcr == TC_NotApplicable) {
2794-
// ... or if that is not possible, a static_cast, ignoring const and
2795-
// addr space, ...
2796-
tcr = TryStaticCast(Self, SrcExpr, DestType, CCK, OpRange, msg, Kind,
2797-
BasePath, ListInitialization);
2798-
if (SrcExpr.isInvalid())
2799-
return;
2810+
// FIXME: Bound member function to function pointer conversion is blocked
2811+
// by an immediate error inside ``Sema::CheckPlaceholderExpr``.
2812+
if (!isBoundPMFConversion()) {
2813+
// ... or if that is not possible, a static_cast, ignoring const and
2814+
// addr space, ...
2815+
tcr = TryStaticCast(Self, SrcExpr, DestType, CCK, OpRange, msg, Kind,
2816+
BasePath, ListInitialization);
2817+
if (SrcExpr.isInvalid())
2818+
return;
2819+
}
28002820

28012821
if (tcr == TC_NotApplicable) {
28022822
// ... and finally a reinterpret_cast, ignoring const and addr space.

clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -524,7 +524,8 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
524524
case CK_VectorSplat:
525525
case CK_HLSLElementwiseCast:
526526
case CK_HLSLAggregateSplatCast:
527-
case CK_HLSLVectorTruncation: {
527+
case CK_HLSLVectorTruncation:
528+
case CK_BoundMemberFunctionToFunctionPointer: {
528529
QualType resultType = CastE->getType();
529530
if (CastE->isGLValue())
530531
resultType = getContext().getPointerType(resultType);

0 commit comments

Comments
 (0)