Skip to content

[Clang] Add support for GCC bound member functions extension #135649

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
^^^^^^^^^^^^^^^^^^^^^

Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/AST/OperationKinds.def
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
32 changes: 16 additions & 16 deletions clang/include/clang/Basic/DiagnosticGroups.td
Original file line number Diff line number Diff line change
Expand Up @@ -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">;

Expand Down Expand Up @@ -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">;

Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -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">;
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/CIR/Dialect/IR/CIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -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>;
Expand Down
7 changes: 7 additions & 0 deletions clang/lib/AST/Expr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/AST/ExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down
1 change: 1 addition & 0 deletions clang/lib/CIR/CodeGen/CIRGenExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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?
Expand Down
1 change: 1 addition & 0 deletions clang/lib/CodeGen/CGExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/CodeGen/CGExprAgg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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;

Expand Down
1 change: 1 addition & 0 deletions clang/lib/CodeGen/CGExprComplex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
1 change: 1 addition & 0 deletions clang/lib/CodeGen/CGExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
32 changes: 32 additions & 0 deletions clang/lib/CodeGen/CGExprScalar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down
22 changes: 20 additions & 2 deletions clang/lib/CodeGen/ItaniumCXXABI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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();

Expand All @@ -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);
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Edit/RewriteObjCFoundationAPI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
60 changes: 40 additions & 20 deletions clang/lib/Sema/SemaCast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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".
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -2827,12 +2843,16 @@ void CastOperation::CheckCXXCStyleCast(bool FunctionalStyle,
return;

if (tcr == TC_NotApplicable) {
// ... or if that is not possible, a static_cast, ignoring const and
// addr space, ...
tcr = TryStaticCast(Self, SrcExpr, DestType, CCK, OpRange, msg, Kind,
BasePath, ListInitialization);
if (SrcExpr.isInvalid())
return;
// FIXME: Bound member function to function pointer conversion is blocked
// by an immediate error inside ``Sema::CheckPlaceholderExpr``.
if (!isBoundPMFConversion()) {
// ... or if that is not possible, a static_cast, ignoring const and
// addr space, ...
tcr = TryStaticCast(Self, SrcExpr, DestType, CCK, OpRange, msg, Kind,
BasePath, ListInitialization);
if (SrcExpr.isInvalid())
return;
}

if (tcr == TC_NotApplicable) {
// ... and finally a reinterpret_cast, ignoring const and addr space.
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -524,7 +524,8 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
case CK_VectorSplat:
case CK_HLSLElementwiseCast:
case CK_HLSLAggregateSplatCast:
case CK_HLSLVectorTruncation: {
case CK_HLSLVectorTruncation:
case CK_BoundMemberFunctionToFunctionPointer: {
QualType resultType = CastE->getType();
if (CastE->isGLValue())
resultType = getContext().getPointerType(resultType);
Expand Down
Loading
Loading